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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1534,6 +1534,7 @@ namespace Barotrauma
|
||||
if (int.TryParse(args[0], out int money))
|
||||
{
|
||||
campaign.Money += money;
|
||||
GameAnalyticsManager.AddMoneyGainedEvent(money, GameAnalyticsManager.MoneySource.Cheat, "console");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace Barotrauma
|
||||
if (GameMain.GameSession?.GameMode is CampaignMode campaign)
|
||||
{
|
||||
campaign.Money += Amount;
|
||||
GameAnalyticsManager.AddMoneyGainedEvent(Amount, GameAnalyticsManager.MoneySource.Event, ParentEvent.Prefab.Identifier);
|
||||
#if SERVER
|
||||
(campaign as MultiPlayerCampaign).LastUpdateID++;
|
||||
#endif
|
||||
|
||||
@@ -152,7 +152,7 @@ namespace Barotrauma
|
||||
if (spawnPoint is WayPoint wp && wp.CurrentHull != null && wp.CurrentHull.Rect.Width > 100)
|
||||
{
|
||||
spawnPos = new Vector2(
|
||||
MathHelper.Clamp(wp.WorldPosition.X + Rand.Range(-200, 200), wp.CurrentHull.WorldRect.X + 50, wp.CurrentHull.WorldRect.Right - 50),
|
||||
MathHelper.Clamp(wp.WorldPosition.X + Rand.Range(-200, 201), wp.CurrentHull.WorldRect.X + 50, wp.CurrentHull.WorldRect.Right - 50),
|
||||
wp.CurrentHull.WorldRect.Y - wp.CurrentHull.Rect.Height + 16.0f);
|
||||
}
|
||||
var item = new Item(itemPrefab, spawnPos, null);
|
||||
|
||||
@@ -319,6 +319,19 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Character character in characters)
|
||||
{
|
||||
if (character.Inventory == null) { continue; }
|
||||
foreach (Item item in character.Inventory.AllItemsMod)
|
||||
{
|
||||
//item didn't spawn with the characters -> drop it
|
||||
if (!characterItems.Any(c => c.Value.Contains(item)))
|
||||
{
|
||||
item.Drop(character);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// characters that survived will take their items with them, in case players tried to be crafty and steal them
|
||||
// this needs to run here in case players abort the mission by going back home
|
||||
// TODO: I think this might feel like a bug.
|
||||
|
||||
@@ -378,7 +378,10 @@ namespace Barotrauma
|
||||
crewCharacters.ForEach(c => c.CheckTalents(AbilityEffectType.OnGainMissionMoney, missionMoneyGainMultiplier));
|
||||
crewCharacters.ForEach(c => missionMoneyGainMultiplier.Value += c.GetStatValue(StatTypes.MissionMoneyGainMultiplier));
|
||||
|
||||
campaign.Money += (int)(reward * missionMoneyGainMultiplier.Value);
|
||||
int totalReward = (int)(reward * missionMoneyGainMultiplier.Value);
|
||||
campaign.Money += totalReward;
|
||||
|
||||
GameAnalyticsManager.AddMoneyGainedEvent(totalReward, GameAnalyticsManager.MoneySource.MissionReward, Prefab.Identifier);
|
||||
|
||||
foreach (Character character in crewCharacters)
|
||||
{
|
||||
|
||||
@@ -328,7 +328,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
dir = new Vector2(1, Rand.Range(-1, 1));
|
||||
dir = new Vector2(1, Rand.Range(-1f, 1f));
|
||||
}
|
||||
Vector2 targetPos = spawnPos.Value + dir * offset;
|
||||
var targetWaypoint = waypoints.OrderBy(wp => Vector2.DistanceSquared(wp.WorldPosition, targetPos)).FirstOrDefault();
|
||||
@@ -475,6 +475,7 @@ namespace Barotrauma
|
||||
{
|
||||
scatterAmount = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
string seed = Level.Loaded.Seed + i.ToString();
|
||||
@@ -540,6 +541,13 @@ namespace Barotrauma
|
||||
SwarmBehavior.CreateSwarm(monsters.Cast<AICharacter>());
|
||||
DebugConsole.NewMessage($"Spawned: {ToString()}. Strength: {StringFormatter.FormatZeroDecimal(monsters.Sum(m => m.Params.AI.CombatStrength))}.", Color.LightBlue, debugOnly: true);
|
||||
}
|
||||
|
||||
if (GameMain.GameSession != null)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent(
|
||||
$"MonsterSpawn:{GameMain.GameSession.GameMode?.Preset?.Identifier ?? "none"}:{Level.Loaded?.LevelData?.Biome?.Identifier ?? "none"}:{SpawnPosType}:{speciesName}",
|
||||
value: Timing.TotalTime - GameMain.GameSession.RoundStartTime);
|
||||
}
|
||||
}, delayBetweenSpawns * i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using System;
|
||||
using Barotrauma.Steam;
|
||||
using RestSharp;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public static partial class GameAnalyticsManager
|
||||
static partial class GameAnalyticsManager
|
||||
{
|
||||
public enum Consent
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#nullable enable
|
||||
using Barotrauma.IO;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -9,7 +10,7 @@ using System.Text;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public static partial class GameAnalyticsManager
|
||||
static partial class GameAnalyticsManager
|
||||
{
|
||||
public enum ErrorSeverity
|
||||
{
|
||||
@@ -29,6 +30,61 @@ namespace Barotrauma
|
||||
Fail = 3
|
||||
}
|
||||
|
||||
public enum CustomDimensions01
|
||||
{
|
||||
Vanilla,
|
||||
Modded
|
||||
}
|
||||
|
||||
public enum CustomDimensions02
|
||||
{
|
||||
None,
|
||||
Difficulty0to10,
|
||||
Difficulty10to20,
|
||||
Difficulty20to30,
|
||||
Difficulty30to40,
|
||||
Difficulty40to50,
|
||||
Difficulty50to60,
|
||||
Difficulty60to70,
|
||||
Difficulty70to80,
|
||||
Difficulty80to90,
|
||||
Difficulty90to100,
|
||||
}
|
||||
|
||||
public enum ResourceCurrency
|
||||
{
|
||||
Money
|
||||
}
|
||||
|
||||
public enum ResourceFlowType
|
||||
{
|
||||
Undefined = 0,
|
||||
Source = 1,
|
||||
Sink = 2
|
||||
}
|
||||
|
||||
public enum MoneySource
|
||||
{
|
||||
Unknown,
|
||||
MissionReward,
|
||||
Store,
|
||||
Event,
|
||||
Ability,
|
||||
Cheat
|
||||
}
|
||||
|
||||
public enum MoneySink
|
||||
{
|
||||
Unknown,
|
||||
Store,
|
||||
Service,
|
||||
Crew,
|
||||
SubmarineUpgrade,
|
||||
SubmarineWeapon,
|
||||
SubmarinePurchase,
|
||||
SubmarineSwitch
|
||||
}
|
||||
|
||||
private readonly static HashSet<string> sentEventIdentifiers = new HashSet<string>();
|
||||
|
||||
private class Implementation : IDisposable
|
||||
@@ -69,13 +125,29 @@ namespace Barotrauma
|
||||
internal void AddProgressionEvent(ProgressionStatus status, string progression01, string progression02, string progression03)
|
||||
=> addProgressionEvent03(status, progression01, progression02, progression03);
|
||||
|
||||
private readonly Action<ResourceFlowType, string, float, string, string> addResourceEvent;
|
||||
internal void AddResourceEvent(ResourceFlowType flowType, string currency, float amount, string itemType, string itemId)
|
||||
=> addResourceEvent(flowType, currency, amount, itemType, itemId);
|
||||
|
||||
private readonly Action<string> setCustomDimension01;
|
||||
internal void SetCustomDimension01(string dimension01)
|
||||
=> setCustomDimension01(dimension01);
|
||||
|
||||
private readonly Action<string[]> configureAvailableCustomDimensions01;
|
||||
internal void ConfigureAvailableCustomDimensions01(params string[] customDimensions)
|
||||
=> configureAvailableCustomDimensions01(customDimensions);
|
||||
internal void ConfigureAvailableCustomDimensions01(params CustomDimensions01[] customDimensions)
|
||||
=> configureAvailableCustomDimensions01(customDimensions.Select(d => d.ToString()).ToArray());
|
||||
|
||||
private readonly Action<string> setCustomDimension02;
|
||||
internal void SetCustomDimension02(string dimension02)
|
||||
=> setCustomDimension02(dimension02);
|
||||
|
||||
private readonly Action<string[]> configureAvailableCustomDimensions02;
|
||||
internal void ConfigureAvailableCustomDimensions02(params CustomDimensions02[] customDimensions)
|
||||
=> configureAvailableCustomDimensions02(customDimensions.Select(d => d.ToString()).ToArray());
|
||||
|
||||
private readonly Action<string[]> configureAvailableResourceCurrencies;
|
||||
internal void ConfigureAvailableResourceCurrencies(params ResourceCurrency[] customDimensions)
|
||||
=> configureAvailableResourceCurrencies(customDimensions.Select(d => d.ToString()).ToArray());
|
||||
|
||||
private readonly Action<bool> setEnabledInfoLog;
|
||||
internal void SetEnabledInfoLog(bool enabled)
|
||||
@@ -94,6 +166,7 @@ namespace Barotrauma
|
||||
private readonly object?[] args2 = new object?[2];
|
||||
private readonly object?[] args3 = new object?[3];
|
||||
private readonly object?[] args4 = new object?[4];
|
||||
private readonly object?[] args5 = new object?[5];
|
||||
|
||||
private Action Call(MethodInfo methodInfo)
|
||||
=> () => methodInfo?.Invoke(null, null);
|
||||
@@ -131,6 +204,17 @@ namespace Barotrauma
|
||||
args4[3] = arg4;
|
||||
methodInfo.Invoke(null, args4);
|
||||
};
|
||||
|
||||
private Action<T1, T2, T3, T4, T5> Call<T1, T2, T3, T4, T5>(MethodInfo methodInfo)
|
||||
=> (T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) =>
|
||||
{
|
||||
args5[0] = arg1;
|
||||
args5[1] = arg2;
|
||||
args5[2] = arg3;
|
||||
args5[3] = arg4;
|
||||
args5[4] = arg5;
|
||||
methodInfo.Invoke(null, args5);
|
||||
};
|
||||
#endregion
|
||||
|
||||
private AssemblyLoadContext? loadContext;
|
||||
@@ -165,9 +249,15 @@ namespace Barotrauma
|
||||
var mainClass = getType(MainClass);
|
||||
var errorSeverityEnumType = getType($"{EnumPrefix}{nameof(ErrorSeverity)}");
|
||||
var progressionStatusEnumType = getType($"{EnumPrefix}{nameof(ProgressionStatus)}");
|
||||
var resourceFlowTypeEnumType = getType($"{EnumPrefix}{nameof(ResourceFlowType)}");
|
||||
|
||||
MethodInfo getMethod(string name, Type[] types)
|
||||
{
|
||||
foreach (var me in mainClass.GetMethods())
|
||||
{
|
||||
var aksjdnakjsdnf = me;
|
||||
}
|
||||
|
||||
return mainClass?.GetMethod(name, BindingFlags.Public | BindingFlags.Static, binder: null, types: types, modifiers: null)
|
||||
?? throw new Exception($"Could not find method \"{name}\" with types {string.Join(',', types.Select(t => t.Name))}");
|
||||
}
|
||||
@@ -190,10 +280,20 @@ namespace Barotrauma
|
||||
new Type[] { progressionStatusEnumType, typeof(string), typeof(string) }));
|
||||
addProgressionEvent03 = Call<ProgressionStatus, string, string, string>(getMethod(nameof(AddProgressionEvent),
|
||||
new Type[] { progressionStatusEnumType, typeof(string), typeof(string), typeof(string) }));
|
||||
|
||||
setCustomDimension01 = Call<string>(getMethod(nameof(SetCustomDimension01),
|
||||
new Type[] { typeof(string) }));
|
||||
configureAvailableCustomDimensions01 = Call<string[]>(getMethod(nameof(ConfigureAvailableCustomDimensions01),
|
||||
new Type[] { typeof(string[]) }));
|
||||
setCustomDimension02 = Call<string>(getMethod(nameof(SetCustomDimension02),
|
||||
new Type[] { typeof(string) }));
|
||||
configureAvailableCustomDimensions02 = Call<string[]>(getMethod(nameof(ConfigureAvailableCustomDimensions02),
|
||||
new Type[] { typeof(string[]) }));
|
||||
|
||||
configureAvailableResourceCurrencies = Call<string[]>(getMethod(nameof(ConfigureAvailableResourceCurrencies),
|
||||
new Type[] { typeof(string[]) }));
|
||||
addResourceEvent = Call<ResourceFlowType, string, float, string, string>(getMethod(nameof(AddResourceEvent),
|
||||
new Type[] { resourceFlowTypeEnumType, typeof(string), typeof(float), typeof(string), typeof(string) }));
|
||||
setEnabledInfoLog = Call<bool>(getMethod(nameof(SetEnabledInfoLog),
|
||||
new Type[] { typeof(bool) }));
|
||||
|
||||
@@ -204,8 +304,7 @@ namespace Barotrauma
|
||||
private void OnQuit()
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
{
|
||||
if (assembly != null) { onQuit?.Invoke(); }
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -298,10 +397,40 @@ namespace Barotrauma
|
||||
loadedImplementation?.AddProgressionEvent(progressionStatus, progression01, progression02, progression03);
|
||||
}
|
||||
|
||||
public static void SetCustomDimension01(string dimension)
|
||||
public static void SetCustomDimension01(CustomDimensions01 dimension)
|
||||
{
|
||||
if (!SendUserStatistics) { return; }
|
||||
loadedImplementation?.SetCustomDimension01(dimension);
|
||||
loadedImplementation?.SetCustomDimension01(dimension.ToString());
|
||||
}
|
||||
|
||||
public static void SetCurrentLevel(LevelData levelData)
|
||||
{
|
||||
if (!SendUserStatistics) { return; }
|
||||
|
||||
CustomDimensions02 customDimension = CustomDimensions02.None;
|
||||
if (levelData != null)
|
||||
{
|
||||
float levelDifficulty = levelData.Difficulty;
|
||||
customDimension = (CustomDimensions02)MathHelper.Clamp((int)(levelDifficulty / 10) + 1, 0, Enum.GetValues(typeof(CustomDimensions02)).Length - 1);
|
||||
}
|
||||
|
||||
loadedImplementation?.SetCustomDimension02(customDimension.ToString());
|
||||
}
|
||||
|
||||
public static void AddMoneyGainedEvent(int amount, MoneySource moneySource, string eventId)
|
||||
{
|
||||
AddResourceEvent(ResourceFlowType.Source, ResourceCurrency.Money, amount, moneySource.ToString(), eventId);
|
||||
}
|
||||
|
||||
public static void AddMoneySpentEvent(int amount, MoneySink moneySink, string eventId)
|
||||
{
|
||||
AddResourceEvent(ResourceFlowType.Sink, ResourceCurrency.Money, amount, moneySink.ToString(), eventId);
|
||||
}
|
||||
|
||||
private static void AddResourceEvent(ResourceFlowType flowType, ResourceCurrency currency, float amount, string eventType, string eventId)
|
||||
{
|
||||
if (!SendUserStatistics) { return; }
|
||||
loadedImplementation?.AddResourceEvent(flowType, currency.ToString(), amount, eventType, eventId);
|
||||
}
|
||||
|
||||
private static void Init()
|
||||
@@ -359,7 +488,8 @@ namespace Barotrauma
|
||||
+ exeName + ":"
|
||||
+ AssemblyInfo.GitRevision + ":"
|
||||
+ buildConfiguration);
|
||||
loadedImplementation?.ConfigureAvailableCustomDimensions01("singleplayer", "multiplayer", "editor");
|
||||
loadedImplementation?.ConfigureAvailableCustomDimensions01(Enum.GetValues(typeof(CustomDimensions01)).Cast<CustomDimensions01>().ToArray());
|
||||
loadedImplementation?.ConfigureAvailableResourceCurrencies(Enum.GetValues(typeof(ResourceCurrency)).Cast<ResourceCurrency>().ToArray());
|
||||
|
||||
InitKeys();
|
||||
|
||||
@@ -380,15 +510,16 @@ namespace Barotrauma
|
||||
var allPackages = GameMain.Config?.AllEnabledPackages.ToList();
|
||||
if (allPackages?.Count > 0)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder("ContentPackage: ");
|
||||
int i = 0;
|
||||
List<string> packageNames = new List<string>();
|
||||
foreach (ContentPackage cp in allPackages)
|
||||
{
|
||||
string trimmedName = cp.Name.Replace(":", "").Replace(" ", "");
|
||||
sb.Append(trimmedName.Substring(0, Math.Min(32, trimmedName.Length)));
|
||||
if (i < allPackages.Count - 1) { sb.Append(" "); }
|
||||
string sanitizedName = cp.Name.Replace(":", "").Replace(" ", "");
|
||||
sanitizedName = sanitizedName.Substring(0, Math.Min(32, sanitizedName.Length));
|
||||
packageNames.Add(sanitizedName);
|
||||
loadedImplementation?.AddDesignEvent("ContentPackage:" + sanitizedName);
|
||||
}
|
||||
loadedImplementation?.AddDesignEvent(sb.ToString());
|
||||
packageNames.Sort();
|
||||
loadedImplementation?.AddDesignEvent("AllContentPackages:" + string.Join(", ", packageNames));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -132,6 +132,7 @@ namespace Barotrauma
|
||||
// Exchange money
|
||||
var itemValue = item.Quantity * buyValues[item.ItemPrefab];
|
||||
campaign.Money -= itemValue;
|
||||
GameAnalyticsManager.AddMoneySpentEvent(itemValue, GameAnalyticsManager.MoneySink.Store, item.ItemPrefab.Identifier);
|
||||
Location.StoreCurrentBalance += itemValue;
|
||||
|
||||
if (removeFromCrate)
|
||||
@@ -291,7 +292,7 @@ namespace Barotrauma
|
||||
float floorPos = hull.Rect.Y - hull.Rect.Height;
|
||||
|
||||
Vector2 position = new Vector2(
|
||||
hull.Rect.Width > 40 ? Rand.Range(hull.Rect.X + 20, hull.Rect.Right - 20) : hull.Rect.Center.X,
|
||||
hull.Rect.Width > 40 ? Rand.Range(hull.Rect.X + 20f, hull.Rect.Right - 20f) : hull.Rect.Center.X,
|
||||
floorPos);
|
||||
|
||||
//check where the actual floor structure is in case the bottom of the hull extends below it
|
||||
|
||||
@@ -37,7 +37,6 @@ namespace Barotrauma
|
||||
{
|
||||
IsSinglePlayer = isSinglePlayer;
|
||||
conversationTimer = 5.0f;
|
||||
|
||||
InitProjectSpecific();
|
||||
}
|
||||
|
||||
@@ -100,10 +99,10 @@ namespace Barotrauma
|
||||
foreach (XElement characterElement in element.Elements())
|
||||
{
|
||||
if (!characterElement.Name.ToString().Equals("character", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
|
||||
CharacterInfo characterInfo = new CharacterInfo(characterElement);
|
||||
#if CLIENT
|
||||
if (characterElement.GetAttributeBool("lastcontrolled", false)) { characterInfo.LastControlled = true; }
|
||||
characterInfo.CrewListIndex = characterElement.GetAttributeInt("crewlistindex", -1);
|
||||
#endif
|
||||
characterInfos.Add(characterInfo);
|
||||
foreach (XElement subElement in characterElement.Elements())
|
||||
@@ -133,7 +132,7 @@ namespace Barotrauma
|
||||
characterInfos.Remove(characterInfo);
|
||||
}
|
||||
|
||||
public void AddCharacter(Character character)
|
||||
public void AddCharacter(Character character, bool sortCrewList = true)
|
||||
{
|
||||
if (character.Removed)
|
||||
{
|
||||
@@ -155,7 +154,11 @@ namespace Barotrauma
|
||||
characterInfos.Add(character.Info);
|
||||
}
|
||||
#if CLIENT
|
||||
AddCharacterToCrewList(character);
|
||||
var characterComponent = AddCharacterToCrewList(character);
|
||||
if (sortCrewList)
|
||||
{
|
||||
SortCrewList();
|
||||
}
|
||||
if (character.CurrentOrders != null)
|
||||
{
|
||||
foreach (var order in character.CurrentOrders)
|
||||
@@ -254,12 +257,16 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
AddCharacter(character);
|
||||
AddCharacter(character, sortCrewList: false);
|
||||
#if CLIENT
|
||||
if (IsSinglePlayer && (Character.Controlled == null || character.Info.LastControlled)) { Character.Controlled = character; }
|
||||
#endif
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
if (IsSinglePlayer) { SortCrewList(); }
|
||||
#endif
|
||||
|
||||
//longer delay in multiplayer to prevent the server from triggering NPC conversations while the players are still loading the round
|
||||
conversationTimer = IsSinglePlayer ? Rand.Range(5.0f, 10.0f) : Rand.Range(45.0f, 60.0f);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Barotrauma
|
||||
public Faction(CampaignMetadata metadata, FactionPrefab prefab)
|
||||
{
|
||||
Prefab = prefab;
|
||||
Reputation = new Reputation(metadata, $"faction.{prefab.Identifier}", prefab.MinReputation, prefab.MaxReputation, prefab.InitialReputation);
|
||||
Reputation = new Reputation(metadata, this, prefab.MinReputation, prefab.MaxReputation, prefab.InitialReputation);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,9 +35,22 @@ namespace Barotrauma
|
||||
private set
|
||||
{
|
||||
if (MathUtils.NearlyEqual(Value, value)) { return; }
|
||||
|
||||
float prevValue = Value;
|
||||
|
||||
Metadata.SetValue(metaDataIdentifier, Math.Clamp(value, MinReputation, MaxReputation));
|
||||
OnReputationValueChanged?.Invoke();
|
||||
OnAnyReputationValueChanged?.Invoke();
|
||||
#if CLIENT
|
||||
int increase = (int)Value - (int)prevValue;
|
||||
if (increase != 0 && Character.Controlled != null)
|
||||
{
|
||||
Character.Controlled.AddMessage(
|
||||
TextManager.GetWithVariable("reputationgainnotification", "[reputationname]", Location?.Name ?? Faction.Prefab.Name),
|
||||
increase > 0 ? GUI.Style.Green : GUI.Style.Red,
|
||||
playSound: true, Identifier, increase, lifetime: 5.0f);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,15 +76,32 @@ namespace Barotrauma
|
||||
public Action OnReputationValueChanged;
|
||||
public static Action OnAnyReputationValueChanged;
|
||||
|
||||
public Reputation(CampaignMetadata metadata, string identifier, int minReputation, int maxReputation, int initialReputation)
|
||||
public readonly Faction Faction;
|
||||
public readonly Location Location;
|
||||
|
||||
|
||||
public Reputation(CampaignMetadata metadata, Location location, string identifier, int minReputation, int maxReputation, int initialReputation)
|
||||
: this(metadata, null, location, identifier, minReputation, maxReputation, initialReputation)
|
||||
{
|
||||
}
|
||||
|
||||
public Reputation(CampaignMetadata metadata, Faction faction, int minReputation, int maxReputation, int initialReputation)
|
||||
: this(metadata, faction, null, $"faction.{faction.Prefab.Identifier}", minReputation, maxReputation, initialReputation)
|
||||
{
|
||||
}
|
||||
|
||||
private Reputation(CampaignMetadata metadata, Faction faction, Location location, string identifier, int minReputation, int maxReputation, int initialReputation)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(metadata != null);
|
||||
System.Diagnostics.Debug.Assert(faction != null || location != null);
|
||||
Metadata = metadata;
|
||||
Identifier = identifier.ToLowerInvariant();
|
||||
metaDataIdentifier = $"reputation.{Identifier}";
|
||||
MinReputation = minReputation;
|
||||
MaxReputation = maxReputation;
|
||||
InitialReputation = initialReputation;
|
||||
Faction = faction;
|
||||
Location = location;
|
||||
}
|
||||
|
||||
public string GetReputationName()
|
||||
|
||||
@@ -78,6 +78,9 @@ namespace Barotrauma
|
||||
//there can be no events before this time has passed during the 1st campaign round
|
||||
const float FirstRoundEventDelay = 0.0f;
|
||||
|
||||
public double TotalPlayTime;
|
||||
public int TotalPassedLevels;
|
||||
|
||||
public enum InteractionType { None, Talk, Examine, Map, Crew, Store, Repair, Upgrade, PurchaseSub, MedicalClinic }
|
||||
|
||||
public readonly CargoManager CargoManager;
|
||||
@@ -92,7 +95,7 @@ namespace Barotrauma
|
||||
|
||||
public CampaignSettings Settings;
|
||||
|
||||
private List<Mission> extraMissions = new List<Mission>();
|
||||
private readonly List<Mission> extraMissions = new List<Mission>();
|
||||
|
||||
public enum TransitionType
|
||||
{
|
||||
@@ -690,11 +693,13 @@ namespace Barotrauma
|
||||
|
||||
GameAnalyticsManager.AddProgressionEvent(
|
||||
GameAnalyticsManager.ProgressionStatus.Complete,
|
||||
Name ?? "none");
|
||||
Preset?.Identifier ?? "none");
|
||||
string eventId = "FinishCampaign:";
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "Submarine:" + (Submarine.MainSub?.Info?.Name ?? "none"));
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "CrewSize:" + (CrewManager?.CharacterInfos?.Count() ?? 0));
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "Money", Money);
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "Money", Money);
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "Playtime", TotalPlayTime);
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "PassedLevels", TotalPassedLevels);
|
||||
}
|
||||
|
||||
protected virtual void EndCampaignProjSpecific() { }
|
||||
@@ -707,12 +712,14 @@ namespace Barotrauma
|
||||
location.RemoveHireableCharacter(characterInfo);
|
||||
CrewManager.AddCharacterInfo(characterInfo);
|
||||
Money -= characterInfo.Salary;
|
||||
GameAnalyticsManager.AddMoneySpentEvent(characterInfo.Salary, GameAnalyticsManager.MoneySink.Crew, characterInfo.Job?.Prefab.Identifier ?? "unknown");
|
||||
return true;
|
||||
}
|
||||
|
||||
private void NPCInteract(Character npc, Character interactor)
|
||||
{
|
||||
if (!npc.AllowCustomInteract) { return; }
|
||||
GameAnalyticsManager.AddDesignEvent("CampaignInteraction:" + Preset.Identifier + ":" + npc.CampaignInteractionType);
|
||||
NPCInteractProjSpecific(npc, interactor);
|
||||
string coroutineName = "DoCharacterWait." + (npc?.ID ?? Entity.NullEntityID);
|
||||
if (!CoroutineManager.IsCoroutineRunning(coroutineName))
|
||||
@@ -876,6 +883,19 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
public abstract void Save(XElement element);
|
||||
|
||||
protected void LoadStats(XElement element)
|
||||
{
|
||||
TotalPlayTime = element.GetAttributeDouble(nameof(TotalPlayTime).ToLowerInvariant(), 0);
|
||||
TotalPassedLevels = element.GetAttributeInt(nameof(TotalPassedLevels).ToLowerInvariant(), 0);
|
||||
}
|
||||
|
||||
protected XElement SaveStats()
|
||||
{
|
||||
return new XElement("stats",
|
||||
new XAttribute(nameof(TotalPlayTime).ToLowerInvariant(), TotalPlayTime),
|
||||
new XAttribute(nameof(TotalPassedLevels).ToLowerInvariant(), TotalPassedLevels));
|
||||
}
|
||||
|
||||
public void LogState()
|
||||
{
|
||||
|
||||
@@ -126,6 +126,10 @@ namespace Barotrauma
|
||||
{
|
||||
case "campaignsettings":
|
||||
Settings = new CampaignSettings(subElement);
|
||||
#if CLIENT
|
||||
GameMain.NetworkMember.ServerSettings.MaxMissionCount = Settings.MaxMissionCount;
|
||||
GameMain.NetworkMember.ServerSettings.RadiationEnabled = Settings.RadiationEnabled;
|
||||
#endif
|
||||
break;
|
||||
case "map":
|
||||
if (map == null)
|
||||
@@ -159,6 +163,9 @@ namespace Barotrauma
|
||||
case "pets":
|
||||
petsElement = subElement;
|
||||
break;
|
||||
case "stats":
|
||||
LoadStats(subElement);
|
||||
break;
|
||||
#if SERVER
|
||||
case "savedexperiencepoints":
|
||||
foreach (XElement savedExp in subElement.Elements())
|
||||
|
||||
@@ -22,6 +22,8 @@ namespace Barotrauma
|
||||
|
||||
public double RoundStartTime;
|
||||
|
||||
public double TimeSpentCleaning, TimeSpentPainting;
|
||||
|
||||
private readonly List<Mission> missions = new List<Mission>();
|
||||
public IEnumerable<Mission> Missions { get { return missions; } }
|
||||
|
||||
@@ -276,6 +278,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
Campaign.Money -= cost;
|
||||
GameAnalyticsManager.AddMoneySpentEvent(cost, GameAnalyticsManager.MoneySink.SubmarineSwitch, newSubmarine.Name);
|
||||
|
||||
((CampaignMode)GameMode).PendingSubmarineSwitch = newSubmarine;
|
||||
return newSubmarine;
|
||||
@@ -288,6 +291,7 @@ namespace Barotrauma
|
||||
if (!OwnedSubmarines.Any(s => s.Name == newSubmarine.Name))
|
||||
{
|
||||
Campaign.Money -= newSubmarine.Price;
|
||||
GameAnalyticsManager.AddMoneySpentEvent(newSubmarine.Price, GameAnalyticsManager.MoneySink.SubmarinePurchase, newSubmarine.Name);
|
||||
OwnedSubmarines.Add(newSubmarine);
|
||||
}
|
||||
}
|
||||
@@ -409,7 +413,7 @@ namespace Barotrauma
|
||||
|
||||
GameAnalyticsManager.AddProgressionEvent(
|
||||
GameAnalyticsManager.ProgressionStatus.Start,
|
||||
GameMode?.Name ?? "none");
|
||||
GameMode?.Preset?.Identifier ?? "none");
|
||||
|
||||
string eventId = "StartRound:" + (GameMode?.Preset?.Identifier ?? "none") + ":";
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "Submarine:" + (Submarine.MainSub?.Info?.Name ?? "none"));
|
||||
@@ -419,17 +423,39 @@ namespace Barotrauma
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "MissionType:" + (mission.Prefab.Type.ToString() ?? "none") + ":" + mission.Prefab.Identifier);
|
||||
}
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "LevelType:" + (Level.Loaded?.Type.ToString() ?? "none"));
|
||||
if (Level.Loaded != null)
|
||||
{
|
||||
string levelId = Level.Loaded.Type == LevelData.LevelType.Outpost ?
|
||||
Level.Loaded.StartOutpost?.Info?.OutpostGenerationParams?.Identifier :
|
||||
Level.Loaded.GenerationParams?.Identifier;
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "LevelType:" + Level.Loaded.Type.ToString() + ":" + (levelId ?? "null"));
|
||||
}
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "Biome:" + (Level.Loaded?.LevelData?.Biome?.Identifier ?? "none"));
|
||||
#if CLIENT
|
||||
if (GameMode is TutorialMode tutorialMode)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + tutorialMode.Tutorial.Identifier);
|
||||
if (GameMain.IsFirstLaunch)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent("FirstLaunch:" + eventId + tutorialMode.Tutorial.Identifier);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (GameMode is CampaignMode campaignMode)
|
||||
{
|
||||
if (campaignMode.Map?.Radiation != null && campaignMode.Map.Radiation.Enabled)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "RadiationEnabled");
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "Radiation:Enabled");
|
||||
}
|
||||
else
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "RadiationDisabled");
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "Radiation:Disabled");
|
||||
}
|
||||
bool firstTimeInBiome = Map != null && !Map.Connections.Any(c => c.Passed && c.Biome == LevelData.Biome);
|
||||
if (firstTimeInBiome)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + (Level.Loaded?.LevelData?.Biome?.Identifier ?? "none") + "Discovered:Playtime", campaignMode.TotalPlayTime);
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + (Level.Loaded?.LevelData?.Biome?.Identifier ?? "none") + "Discovered:PassedLevels", campaignMode.TotalPassedLevels);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -759,38 +785,29 @@ namespace Barotrauma
|
||||
GameMode?.End(transitionType);
|
||||
EventManager?.EndRound();
|
||||
StatusEffect.StopAll();
|
||||
missions.Clear();
|
||||
IsRunning = false;
|
||||
|
||||
|
||||
bool success = false;
|
||||
#if CLIENT
|
||||
success = CrewManager.GetCharacters().Any(c => !c.IsDead);
|
||||
bool success = CrewManager.GetCharacters().Any(c => !c.IsDead);
|
||||
#else
|
||||
success = GameMain.Server.ConnectedClients.Any(c => c.InGame && c.Character != null && !c.Character.IsDead);
|
||||
bool success = GameMain.Server.ConnectedClients.Any(c => c.InGame && c.Character != null && !c.Character.IsDead);
|
||||
#endif
|
||||
double roundDuration = Timing.TotalTime - RoundStartTime;
|
||||
GameAnalyticsManager.AddProgressionEvent(
|
||||
success ? GameAnalyticsManager.ProgressionStatus.Complete : GameAnalyticsManager.ProgressionStatus.Fail,
|
||||
GameMode?.Name ?? "none",
|
||||
roundDuration);
|
||||
string eventId = "EndRound:GameMode:" + (GameMode?.Name ?? "none") + ":";
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "Submarine:" + (Submarine.MainSub?.Info?.Name ?? "none"), roundDuration);
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "GameMode:" + (GameMode?.Name ?? "none"), roundDuration);
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "CrewSize:" + (CrewManager?.CharacterInfos?.Count() ?? 0), roundDuration);
|
||||
foreach (Mission mission in missions)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "MissionType:" + (mission.Prefab.Type.ToString() ?? "none") + ":" + mission.Prefab.Identifier + ":" + (mission.Completed ? "Completed" : "Failed"), roundDuration);
|
||||
}
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "LevelType:" + (Level.Loaded?.Type.ToString() ?? "none"), roundDuration);
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "Biome:" + (Level.Loaded?.LevelData?.Biome?.Identifier ?? "none"), roundDuration);
|
||||
string eventId = "EndRound:" + (GameMode?.Preset?.Identifier ?? "none") + ":";
|
||||
LogEndRoundStats(eventId);
|
||||
if (GameMode is CampaignMode campaignMode)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "MoneyEarned", campaignMode.Money - prevMoney);
|
||||
campaignMode.TotalPlayTime += roundDuration;
|
||||
}
|
||||
#if CLIENT
|
||||
HintManager.OnRoundEnded();
|
||||
#endif
|
||||
missions.Clear();
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -798,6 +815,82 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void LogEndRoundStats(string eventId)
|
||||
{
|
||||
double roundDuration = Timing.TotalTime - RoundStartTime;
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "Submarine:" + (Submarine.MainSub?.Info?.Name ?? "none"), roundDuration);
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "GameMode:" + (GameMode?.Name ?? "none"), roundDuration);
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "CrewSize:" + (CrewManager?.CharacterInfos?.Count() ?? 0), roundDuration);
|
||||
foreach (Mission mission in missions)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "MissionType:" + (mission.Prefab.Type.ToString() ?? "none") + ":" + mission.Prefab.Identifier + ":" + (mission.Completed ? "Completed" : "Failed"), roundDuration);
|
||||
}
|
||||
if (Level.Loaded != null)
|
||||
{
|
||||
string levelId = Level.Loaded.Type == LevelData.LevelType.Outpost ?
|
||||
Level.Loaded.StartOutpost?.Info?.OutpostGenerationParams?.Identifier :
|
||||
Level.Loaded.GenerationParams?.Identifier;
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "LevelType:" + (Level.Loaded?.Type.ToString() ?? "none" + ":" + (levelId ?? "null")), roundDuration);
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "Biome:" + (Level.Loaded?.LevelData?.Biome?.Identifier ?? "none"), roundDuration);
|
||||
}
|
||||
|
||||
if (Submarine.MainSub != null)
|
||||
{
|
||||
Dictionary<ItemPrefab, int> submarineInventory = new Dictionary<ItemPrefab, int>();
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
var rootContainer = item.GetRootContainer() ?? item;
|
||||
if (rootContainer.Submarine?.Info == null || rootContainer.Submarine.Info.Type != SubmarineType.Player) { continue; }
|
||||
if (rootContainer.Submarine != Submarine.MainSub && !Submarine.MainSub.DockedTo.Contains(rootContainer.Submarine)) { continue; }
|
||||
|
||||
var holdable = item.GetComponent<Holdable>();
|
||||
if (holdable == null || holdable.Attached) { continue; }
|
||||
var wire = item.GetComponent<Wire>();
|
||||
if (wire != null && wire.Connections.Any(c => c != null)) { continue; }
|
||||
|
||||
if (!submarineInventory.ContainsKey(item.Prefab))
|
||||
{
|
||||
submarineInventory.Add(item.Prefab, 0);
|
||||
}
|
||||
submarineInventory[item.Prefab]++;
|
||||
}
|
||||
foreach (var subItem in submarineInventory)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "SubmarineInventory:" + subItem.Key.Identifier, subItem.Value);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Character c in GetSessionCrewCharacters())
|
||||
{
|
||||
foreach (var itemSelectedDuration in c.ItemSelectedDurations)
|
||||
{
|
||||
string characterType = "Unknown";
|
||||
if (c.IsBot)
|
||||
{
|
||||
characterType = "Bot";
|
||||
}
|
||||
else if (c.IsPlayer)
|
||||
{
|
||||
characterType = "Player";
|
||||
}
|
||||
GameAnalyticsManager.AddDesignEvent("TimeSpentOnDevices:" + (GameMode?.Preset?.Identifier ?? "none") + ":" + characterType + ":" + (c.Info?.Job?.Prefab.Identifier ?? "NoJob") + ":" + itemSelectedDuration.Key.Identifier, itemSelectedDuration.Value);
|
||||
}
|
||||
}
|
||||
#if CLIENT
|
||||
if (GameMode is TutorialMode tutorialMode)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + tutorialMode.Tutorial.Identifier);
|
||||
if (GameMain.IsFirstLaunch)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent("FirstLaunch:" + eventId + tutorialMode.Tutorial.Identifier);
|
||||
}
|
||||
}
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "TimeSpentCleaning", TimeSpentCleaning);
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "TimeSpentPainting", TimeSpentPainting);
|
||||
TimeSpentCleaning = TimeSpentPainting = 0.0;
|
||||
#endif
|
||||
}
|
||||
|
||||
public void KillCharacter(Character character)
|
||||
{
|
||||
#if CLIENT
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace Barotrauma
|
||||
{
|
||||
Identifier = value.Identifier;
|
||||
Strength = (ushort)Math.Ceiling(value.Strength);
|
||||
Price = (ushort)(Strength * value.Prefab.HealCostMultiplier);
|
||||
Price = (ushort)(value.Prefab.BaseHealCost + Strength * value.Prefab.HealCostMultiplier);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,9 +276,15 @@ namespace Barotrauma
|
||||
PendingHeals.Add(crewMember);
|
||||
}
|
||||
|
||||
public static bool IsHealable(Affliction affliction)
|
||||
{
|
||||
return affliction.Prefab.HealableInMedicalClinic && affliction.Strength > GetShowTreshold(affliction);
|
||||
static float GetShowTreshold(Affliction affliction) => Math.Max(0, Math.Min(affliction.Prefab.ShowIconToOthersThreshold, affliction.Prefab.ShowInHealthScannerThreshold));
|
||||
}
|
||||
|
||||
private NetAffliction[] GetAllAfflictions(CharacterHealth health)
|
||||
{
|
||||
IEnumerable<Affliction> rawAfflictions = health.GetAllAfflictions().Where(a => !a.Prefab.IsBuff && a.Strength > GetShowTreshold(a));
|
||||
IEnumerable<Affliction> rawAfflictions = health.GetAllAfflictions().Where(a => IsHealable(a));
|
||||
|
||||
List<NetAffliction> afflictions = new List<NetAffliction>();
|
||||
|
||||
@@ -289,7 +295,7 @@ namespace Barotrauma
|
||||
{
|
||||
afflictions.Remove(foundAffliction);
|
||||
foundAffliction.Strength += (ushort)affliction.Strength;
|
||||
foundAffliction.Price += (ushort)GetAdjustedPrice((int)(affliction.Prefab.HealCostMultiplier * affliction.Strength));
|
||||
foundAffliction.Price += (ushort)GetAdjustedPrice(GetHealPrice(affliction));
|
||||
newAffliction = foundAffliction;
|
||||
}
|
||||
else
|
||||
@@ -303,7 +309,7 @@ namespace Barotrauma
|
||||
|
||||
return afflictions.ToArray();
|
||||
|
||||
static float GetShowTreshold(Affliction affliction) => Math.Max(0, Math.Min(affliction.Prefab.ShowIconToOthersThreshold, affliction.Prefab.ShowInHealthScannerThreshold));
|
||||
static int GetHealPrice(Affliction affliction) => (int)(affliction.Prefab.BaseHealCost + (affliction.Prefab.HealCostMultiplier * affliction.Strength));
|
||||
}
|
||||
|
||||
public int GetTotalCost() => PendingHeals.SelectMany(h => h.Afflictions).Aggregate(0, (current, affliction) => current + affliction.Price);
|
||||
|
||||
@@ -225,6 +225,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
Campaign.Money -= price;
|
||||
GameAnalyticsManager.AddMoneySpentEvent(price, GameAnalyticsManager.MoneySink.SubmarineUpgrade, prefab.Identifier);
|
||||
|
||||
PurchasedUpgrade? upgrade = FindMatchingUpgrade(prefab, category);
|
||||
|
||||
@@ -323,6 +324,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
Campaign.Money -= price;
|
||||
GameAnalyticsManager.AddMoneySpentEvent(price, GameAnalyticsManager.MoneySink.SubmarineWeapon, itemToInstall.Identifier);
|
||||
|
||||
foreach (Item itemToSwap in linkedItems)
|
||||
{
|
||||
|
||||
@@ -488,7 +488,11 @@ namespace Barotrauma
|
||||
var sortedSelected = enabledRegularPackages
|
||||
.OrderBy(p => -ContentPackage.RegularPackages.IndexOf(p))
|
||||
.ToList();
|
||||
if (previousEnabledRegularPackages.SequenceEqual(sortedSelected)) { return; }
|
||||
if (previousEnabledRegularPackages.SequenceEqual(sortedSelected))
|
||||
{
|
||||
CheckModded();
|
||||
return;
|
||||
}
|
||||
enabledRegularPackages.Clear(); enabledRegularPackages.AddRange(sortedSelected);
|
||||
|
||||
CharacterPrefab.Prefabs.SortAll();
|
||||
@@ -508,6 +512,20 @@ namespace Barotrauma
|
||||
{
|
||||
RefreshContentPackageItems(AllEnabledPackages.SelectMany(p => p.Files));
|
||||
}
|
||||
|
||||
CheckModded();
|
||||
|
||||
void CheckModded()
|
||||
{
|
||||
if (AllEnabledPackages.Any(p => p != GameMain.VanillaContent && p.HasMultiplayerIncompatibleContent))
|
||||
{
|
||||
GameAnalyticsManager.SetCustomDimension01(GameAnalyticsManager.CustomDimensions01.Modded);
|
||||
}
|
||||
else
|
||||
{
|
||||
GameAnalyticsManager.SetCustomDimension01(GameAnalyticsManager.CustomDimensions01.Vanilla);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void EnableContentPackageItems(IEnumerable<ContentFile> unorderedFiles)
|
||||
|
||||
@@ -74,7 +74,6 @@ namespace Barotrauma.Items.Components
|
||||
a.Identifier.Equals(Effect, StringComparison.OrdinalIgnoreCase) ||
|
||||
a.AfflictionType.Equals(Effect, StringComparison.OrdinalIgnoreCase)).GetRandom();
|
||||
}
|
||||
Tainted = true;
|
||||
}
|
||||
|
||||
[Serialize(3.0f, false)]
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using FarseerPhysics;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Vector2 = Microsoft.Xna.Framework.Vector2;
|
||||
using Vector4 = Microsoft.Xna.Framework.Vector4;
|
||||
|
||||
@@ -398,8 +398,6 @@ namespace Barotrauma.Items.Components
|
||||
private int flowerVariants;
|
||||
private int leafVariants;
|
||||
private int[] flowerTiles;
|
||||
private const int serverHealthUpdateDelay = 10;
|
||||
private int serverHealthUpdateTimer;
|
||||
|
||||
public float Health
|
||||
{
|
||||
@@ -553,19 +551,21 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (spawnProduct && ProducedItems.Any())
|
||||
{
|
||||
SpawnItem(ProducedItems.RandomElementByWeight(it => it.Probability), spawnPos);
|
||||
SpawnItem(Item, ProducedItems.RandomElementByWeight(it => it.Probability), spawnPos);
|
||||
return;
|
||||
}
|
||||
|
||||
if (spawnSeed)
|
||||
{
|
||||
SpawnItem(ProducedSeed, spawnPos);
|
||||
SpawnItem(Item, ProducedSeed, spawnPos);
|
||||
}
|
||||
|
||||
static void SpawnItem(ProducedItem producedItem, Vector2 pos)
|
||||
static void SpawnItem(Item thisItem, ProducedItem producedItem, Vector2 pos)
|
||||
{
|
||||
if (producedItem.Prefab == null) { return; }
|
||||
|
||||
GameAnalyticsManager.AddDesignEvent("MicroInteraction:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "null") + ":GardeningProduce:" + thisItem.prefab.Identifier + ":" + producedItem.Prefab.Identifier);
|
||||
|
||||
Entity.Spawner?.AddToSpawnQueue(producedItem.Prefab, pos, onSpawned: it =>
|
||||
{
|
||||
foreach (StatusEffect effect in producedItem.StatusEffects)
|
||||
@@ -586,8 +586,13 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (Decayed) { return true; }
|
||||
|
||||
if (0 >= Health)
|
||||
if (Health <= 0)
|
||||
{
|
||||
if (!Decayed)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent("MicroInteraction:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "null") + ":GardeningDied:" + item.prefab.Identifier);
|
||||
}
|
||||
|
||||
Decayed = true;
|
||||
#if CLIENT
|
||||
foreach (VineTile vine in Vines)
|
||||
|
||||
@@ -237,10 +237,13 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
private bool loadedFromXml;
|
||||
public override void Load(XElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
{
|
||||
base.Load(componentElement, usePrefabValues, idRemap);
|
||||
|
||||
loadedFromXml = true;
|
||||
|
||||
if (usePrefabValues)
|
||||
{
|
||||
//this needs to be loaded regardless
|
||||
@@ -536,7 +539,16 @@ namespace Barotrauma.Items.Components
|
||||
else
|
||||
{
|
||||
attachTargetCell = GetAttachTargetCell(150.0f);
|
||||
if (attachTargetCell != null) { IsActive = true; }
|
||||
if (attachTargetCell != null && attachTargetCell.IsDestructible)
|
||||
{
|
||||
attachTargetCell.OnDestroyed += () =>
|
||||
{
|
||||
if (attachTargetCell != null && attachTargetCell.CellType != Voronoi2.CellType.Solid)
|
||||
{
|
||||
Drop(dropConnectedWires: true, dropper: null);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -562,7 +574,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void DeattachFromWall()
|
||||
{
|
||||
if (!attachable) return;
|
||||
if (!attachable) { return; }
|
||||
|
||||
Attached = false;
|
||||
attachTargetCell = null;
|
||||
@@ -733,15 +745,6 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (attachTargetCell != null)
|
||||
{
|
||||
if (attachTargetCell.CellType != Voronoi2.CellType.Solid)
|
||||
{
|
||||
Drop(dropConnectedWires: true, dropper: null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.body == null || !item.body.Enabled) { return; }
|
||||
if (picker == null || !picker.HasEquippedItem(item))
|
||||
{
|
||||
@@ -838,15 +841,25 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
if (item.Submarine != null && item.Submarine.Loading) return;
|
||||
if (item.Submarine != null && item.Submarine.Loading) { return; }
|
||||
OnMapLoaded();
|
||||
item.SetActiveSprite();
|
||||
}
|
||||
|
||||
public override void OnMapLoaded()
|
||||
{
|
||||
if (!attachable) return;
|
||||
if (!attachable) { return; }
|
||||
|
||||
//a mod has overridden the item, and the base item didn't have a Holdable component = a mod made the item movable/detachable
|
||||
if (item.Prefab.IsOverride && !loadedFromXml)
|
||||
{
|
||||
if (attachedByDefault)
|
||||
{
|
||||
AttachToWall();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Attached)
|
||||
{
|
||||
AttachToWall();
|
||||
|
||||
@@ -51,7 +51,11 @@ namespace Barotrauma.Items.Components
|
||||
#else
|
||||
if (deattachTimer >= DeattachDuration)
|
||||
{
|
||||
holdable.DeattachFromWall();
|
||||
if (holdable.Attached)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent("ResourceCollected:" + (GameMain.GameSession?.GameMode?.Name ?? "none") + ":" + item.Prefab.Identifier);
|
||||
holdable.DeattachFromWall();
|
||||
}
|
||||
trigger.Enabled = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -855,6 +855,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
currentTargets.Add(structure);
|
||||
}
|
||||
if (character != null)
|
||||
{
|
||||
currentTargets.Add(character);
|
||||
}
|
||||
effect.Apply(actionType, deltaTime, item, currentTargets);
|
||||
}
|
||||
else if (effect.HasTargetType(StatusEffect.TargetType.Character))
|
||||
|
||||
@@ -570,6 +570,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
GUI.RemoveFromUpdateList(GuiFrame, true);
|
||||
GuiFrame.RectTransform.Parent = null;
|
||||
GuiFrame = null;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -304,7 +304,12 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item.GetComponent<Planter>() != null)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent("MicroInteraction:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "null") + ":GardeningPlanted:" + containedItem.prefab.Identifier);
|
||||
}
|
||||
|
||||
//no need to Update() if this item has no statuseffects and no physics body
|
||||
IsActive = activeContainedItems.Count > 0 || Inventory.AllItems.Any(it => it.body != null);
|
||||
|
||||
@@ -370,29 +370,18 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public Item GetFocusTarget()
|
||||
{
|
||||
Item focusTarget = null;
|
||||
for (int c = 0; c < 2; c++)
|
||||
{
|
||||
//try finding the item to focus on using trigger_out, and if that fails, using position_out
|
||||
string connectionName = c == 0 ? "trigger_out" : "position_out";
|
||||
string signal = c == 0 ? "0" : MathHelper.ToDegrees(targetRotation).ToString("G", CultureInfo.InvariantCulture);
|
||||
if (!item.SendSignal(new Signal(signal, sender: user), connectionName) || focusTarget != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
item.SendSignal(new Signal(MathHelper.ToDegrees(targetRotation).ToString("G", CultureInfo.InvariantCulture), sender: user), "position_out");
|
||||
|
||||
for (int i = item.LastSentSignalRecipients.Count - 1; i >= 0; i--)
|
||||
for (int i = item.LastSentSignalRecipients.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (item.LastSentSignalRecipients[i].Item.Condition <= 0.0f || item.LastSentSignalRecipients[i].IsPower) { continue; }
|
||||
if (item.LastSentSignalRecipients[i].Item.Prefab.FocusOnSelected)
|
||||
{
|
||||
if (item.LastSentSignalRecipients[i].Item.Condition <= 0.0f || item.LastSentSignalRecipients[i].IsPower) { continue; }
|
||||
if (item.LastSentSignalRecipients[i].Item.Prefab.FocusOnSelected)
|
||||
{
|
||||
focusTarget = item.LastSentSignalRecipients[i].Item;
|
||||
break;
|
||||
}
|
||||
return item.LastSentSignalRecipients[i].Item;
|
||||
}
|
||||
}
|
||||
|
||||
return focusTarget;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
|
||||
@@ -300,6 +300,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
GameAnalyticsManager.AddDesignEvent("ItemDeconstructed:" + (GameMain.GameSession?.GameMode?.Name ?? "none") + ":" + targetItem.prefab.Identifier);
|
||||
|
||||
if (targetItem.AllowDeconstruct && allowRemove)
|
||||
{
|
||||
//drop all items that are inside the deconstructed item
|
||||
|
||||
@@ -397,6 +397,7 @@ namespace Barotrauma.Items.Components
|
||||
for (int i = 0; i < (int)fabricationitemAmount.Value; i++)
|
||||
{
|
||||
float outCondition = fabricatedItem.OutCondition;
|
||||
GameAnalyticsManager.AddDesignEvent("ItemFabricated:" + (GameMain.GameSession?.GameMode?.Name ?? "none") + ":" + fabricatedItem.TargetItem.Identifier);
|
||||
if (i < amountFittingContainer)
|
||||
{
|
||||
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * outCondition, quality,
|
||||
|
||||
@@ -314,26 +314,6 @@ namespace Barotrauma.Items.Components
|
||||
return TextManager.GetWithVariable("roomname.subdiroclock", "[dir]", clockDir.ToString());
|
||||
}
|
||||
|
||||
private Vector2 GetTransducerPos()
|
||||
{
|
||||
if (!UseTransducers || connectedTransducers.Count == 0)
|
||||
{
|
||||
//use the position of the sub if the item is static (no body) and inside a sub
|
||||
return item.Submarine != null && item.body == null ? item.Submarine.WorldPosition : item.WorldPosition;
|
||||
}
|
||||
|
||||
Vector2 transducerPosSum = Vector2.Zero;
|
||||
foreach (ConnectedTransducer transducer in connectedTransducers)
|
||||
{
|
||||
if (transducer.Transducer.Item.Submarine != null && CenterOnTransducers)
|
||||
{
|
||||
return transducer.Transducer.Item.Submarine.WorldPosition;
|
||||
}
|
||||
transducerPosSum += transducer.Transducer.Item.WorldPosition;
|
||||
}
|
||||
return transducerPosSum / connectedTransducers.Count;
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(Signal signal, Connection connection)
|
||||
{
|
||||
base.ReceiveSignal(signal, connection);
|
||||
|
||||
@@ -113,6 +113,9 @@ namespace Barotrauma.Items.Components
|
||||
[Serialize(false, true, description: "If true, the recharge speed (and power consumption) of the device goes up exponentially as the recharge rate is increased.")]
|
||||
public bool ExponentialRechargeSpeed { get; set; }
|
||||
|
||||
[Editable(minValue: 0.0f, maxValue: 10.0f, decimals: 2), Serialize(0.5f, true)]
|
||||
public float RechargeAdjustSpeed { get; set; }
|
||||
|
||||
private float efficiency;
|
||||
[Editable(minValue: 0.0f, maxValue: 1.0f, decimals: 2), Serialize(0.95f, true, description: "The amount of power you can get out of a item relative to the amount of power that's put into it.")]
|
||||
public float Efficiency
|
||||
@@ -199,7 +202,14 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
targetRechargeSpeed *= missingCharge;
|
||||
}
|
||||
currPowerConsumption = MathHelper.Lerp(currPowerConsumption, targetRechargeSpeed, 0.05f);
|
||||
if (currPowerConsumption < targetRechargeSpeed)
|
||||
{
|
||||
currPowerConsumption = Math.Min(currPowerConsumption + deltaTime * maxRechargeSpeed * RechargeAdjustSpeed, targetRechargeSpeed);
|
||||
}
|
||||
else
|
||||
{
|
||||
currPowerConsumption = Math.Max(currPowerConsumption - deltaTime * maxRechargeSpeed * RechargeAdjustSpeed, targetRechargeSpeed);
|
||||
}
|
||||
Charge += currPowerConsumption * Math.Min(Voltage, 1.0f) / 3600.0f * efficiency;
|
||||
}
|
||||
|
||||
|
||||
@@ -244,7 +244,6 @@ namespace Barotrauma.Items.Components
|
||||
return picker != null;
|
||||
}
|
||||
|
||||
private static readonly HashSet<Connection> tempConnected = new HashSet<Connection>();
|
||||
protected void RefreshConnections()
|
||||
{
|
||||
var connections = item.Connections;
|
||||
@@ -260,41 +259,51 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
//find all connections that are connected to this one (directly or via another PowerTransfer)
|
||||
tempConnected.Clear();
|
||||
HashSet<Connection> tempConnected;
|
||||
if (!connectedRecipients.ContainsKey(c))
|
||||
{
|
||||
tempConnected = new HashSet<Connection>();
|
||||
connectedRecipients.Add(c, tempConnected);
|
||||
}
|
||||
else
|
||||
{
|
||||
tempConnected = connectedRecipients[c];
|
||||
tempConnected.Clear();
|
||||
//mark all previous recipients as dirty
|
||||
foreach (Connection recipient in tempConnected)
|
||||
{
|
||||
var pt = recipient.Item.GetComponent<PowerTransfer>();
|
||||
if (pt != null) { pt.connectionDirty[recipient] = true; }
|
||||
}
|
||||
}
|
||||
|
||||
tempConnected.Add(c);
|
||||
if (item.Condition > 0.0f)
|
||||
{
|
||||
if (!connectedRecipients.ContainsKey(c))
|
||||
{
|
||||
connectedRecipients.Add(c, tempConnected);
|
||||
}
|
||||
else
|
||||
{
|
||||
//mark all previous recipients as dirty
|
||||
foreach (Connection recipient in connectedRecipients[c])
|
||||
{
|
||||
var pt = recipient.Item.GetComponent<PowerTransfer>();
|
||||
if (pt != null) pt.connectionDirty[recipient] = true;
|
||||
}
|
||||
}
|
||||
|
||||
tempConnected.Add(c);
|
||||
GetConnected(c, tempConnected);
|
||||
}
|
||||
connectedRecipients[c] = tempConnected;
|
||||
|
||||
//go through all the PowerTransfers that we're connected to and set their connections to match the ones we just calculated
|
||||
//(no need to go through the recursive GetConnected method again)
|
||||
foreach (Connection recipient in tempConnected)
|
||||
{
|
||||
if (recipient == c) { continue; }
|
||||
var recipientPowerTransfer = recipient.Item.GetComponent<PowerTransfer>();
|
||||
if (recipientPowerTransfer == null) { continue; }
|
||||
if (!connectedRecipients.ContainsKey(recipient))
|
||||
//go through all the PowerTransfers that we're connected to and set their connections to match the ones we just calculated
|
||||
//(no need to go through the recursive GetConnected method again)
|
||||
foreach (Connection recipient in tempConnected)
|
||||
{
|
||||
connectedRecipients.Add(recipient, tempConnected);
|
||||
if (recipient == c) { continue; }
|
||||
var recipientPowerTransfer = recipient.Item.GetComponent<PowerTransfer>();
|
||||
if (recipientPowerTransfer == null) { continue; }
|
||||
if (!recipientPowerTransfer.connectedRecipients.ContainsKey(recipient))
|
||||
{
|
||||
recipientPowerTransfer.connectedRecipients.Add(recipient, new HashSet<Connection>());
|
||||
}
|
||||
else
|
||||
{
|
||||
recipientPowerTransfer.connectedRecipients[recipient].Clear();
|
||||
}
|
||||
foreach (var connection in tempConnected)
|
||||
{
|
||||
recipientPowerTransfer.connectedRecipients[recipient].Add(connection);
|
||||
}
|
||||
recipientPowerTransfer.connectionDirty[recipient] = false;
|
||||
}
|
||||
recipientPowerTransfer.connectionDirty[recipient] = false;
|
||||
}
|
||||
connectionDirty[c] = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -543,6 +543,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (fixture.Body.UserData is VoronoiCell) { return -1; }
|
||||
if (fixture.Body.UserData is Entity entity && entity.Submarine != submarine) { return -1; }
|
||||
if (fixture.Body.UserData is Limb limb && limb.character?.Submarine != submarine) { return -1; }
|
||||
}
|
||||
|
||||
//ignore level cells if the item and the point of impact are inside a sub
|
||||
|
||||
@@ -87,7 +87,11 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
string signalOut = sendOutput ? output : falseOutput;
|
||||
if (string.IsNullOrEmpty(signalOut)) { return; }
|
||||
if (string.IsNullOrEmpty(signalOut))
|
||||
{
|
||||
IsActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
item.SendSignal(new Signal(signalOut, sender: signalSender[0] ?? signalSender[1]), "signal_out");
|
||||
}
|
||||
@@ -100,11 +104,13 @@ namespace Barotrauma.Items.Components
|
||||
if (signal.value == "0") { return; }
|
||||
timeSinceReceived[0] = 0.0f;
|
||||
signalSender[0] = signal.sender;
|
||||
IsActive = true;
|
||||
break;
|
||||
case "signal_in2":
|
||||
if (signal.value == "0") { return; }
|
||||
timeSinceReceived[1] = 0.0f;
|
||||
signalSender[1] = signal.sender;
|
||||
IsActive = true;
|
||||
break;
|
||||
case "set_output":
|
||||
output = signal.value;
|
||||
|
||||
@@ -23,7 +23,6 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public ButtonTerminal(Item item, XElement element) : base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
RequiredSignalCount = element.GetChildElements("TerminalButton").Count(c => c.GetAttribute("style") != null);
|
||||
if (RequiredSignalCount < 1)
|
||||
{
|
||||
@@ -88,13 +87,13 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
var containers = item.GetComponents<ItemContainer>().ToList();
|
||||
if (containers.Count != 1)
|
||||
var containers = item.GetComponents<ItemContainer>();
|
||||
if (containers.Count() != 1)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in item \"{item.Name}\": the ButtonTerminal component requires exactly one ItemContainer component!");
|
||||
return;
|
||||
}
|
||||
Container = containers[0];
|
||||
Container = containers.FirstOrDefault();
|
||||
|
||||
OnItemLoadedProjSpecific();
|
||||
}
|
||||
|
||||
@@ -66,11 +66,16 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (signalQueue.Count == 0)
|
||||
{
|
||||
IsActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var val in signalQueue)
|
||||
{
|
||||
val.SendTimer -= 1;
|
||||
}
|
||||
|
||||
while (signalQueue.Count > 0 && signalQueue.Peek().SendTimer <= 0)
|
||||
{
|
||||
var signalOut = signalQueue.Peek();
|
||||
@@ -114,6 +119,7 @@ namespace Barotrauma.Items.Components
|
||||
SendDuration = 1
|
||||
};
|
||||
signalQueue.Enqueue(prevQueuedSignal);
|
||||
IsActive = true;
|
||||
break;
|
||||
case "set_delay":
|
||||
if (float.TryParse(signal.value, NumberStyles.Any, CultureInfo.InvariantCulture, out float newDelay))
|
||||
|
||||
@@ -26,6 +26,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public PhysicsBody ParentBody;
|
||||
|
||||
private bool isOn;
|
||||
|
||||
private Turret turret;
|
||||
|
||||
[Serialize(100.0f, true, description: "The range of the emitted light. Higher values are more performance-intensive.", alwaysUseInstanceValues: true),
|
||||
@@ -85,12 +87,13 @@ namespace Barotrauma.Items.Components
|
||||
[Editable, Serialize(false, true, description: "Is the light currently on.", alwaysUseInstanceValues: true)]
|
||||
public bool IsOn
|
||||
{
|
||||
get { return IsActive; }
|
||||
get { return isOn; }
|
||||
set
|
||||
{
|
||||
if (IsActive == value) { return; }
|
||||
if (isOn == value && IsActive == value) { return; }
|
||||
|
||||
IsActive = value;
|
||||
IsActive = isOn = value;
|
||||
SetLightSourceState(value, value ? lightBrightness : 0.0f);
|
||||
OnStateChanged();
|
||||
}
|
||||
}
|
||||
@@ -200,9 +203,8 @@ namespace Barotrauma.Items.Components
|
||||
set
|
||||
{
|
||||
if (base.IsActive == value) { return; }
|
||||
base.IsActive = value;
|
||||
|
||||
SetLightSourceState(value, value ? lightBrightness : 0.0f);
|
||||
base.IsActive = isOn = value;
|
||||
SetLightSourceState(value, value ? lightBrightness : 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,6 +239,23 @@ namespace Barotrauma.Items.Components
|
||||
turret = item.GetComponent<Turret>();
|
||||
}
|
||||
|
||||
public override void OnMapLoaded()
|
||||
{
|
||||
if (item.body == null && powerConsumption <= 0.0f && Parent == null && turret == null &&
|
||||
(statusEffectLists == null || !statusEffectLists.ContainsKey(ActionType.OnActive)) &&
|
||||
(IsActiveConditionals == null || IsActiveConditionals.Count == 0))
|
||||
{
|
||||
lightBrightness = 1.0f;
|
||||
SetLightSourceState(true, lightBrightness);
|
||||
SetLightSourceTransformProjSpecific();
|
||||
base.IsActive = false;
|
||||
isOn = true;
|
||||
#if CLIENT
|
||||
Light.ParentSub = item.Submarine;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (item.AiTarget != null)
|
||||
|
||||
@@ -20,7 +20,11 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
string signalOut = sendOutput ? output : falseOutput;
|
||||
if (string.IsNullOrEmpty(signalOut)) { return; }
|
||||
if (string.IsNullOrEmpty(signalOut))
|
||||
{
|
||||
IsActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
item.SendSignal(new Signal(signalOut, sender: signalSender[0] ?? signalSender[1]), "signal_out");
|
||||
}
|
||||
|
||||
@@ -8,12 +8,15 @@ using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class WifiComponent : ItemComponent
|
||||
partial class WifiComponent : ItemComponent, IServerSerializable
|
||||
{
|
||||
private static readonly List<WifiComponent> list = new List<WifiComponent>();
|
||||
|
||||
const int ChannelMemorySize = 10;
|
||||
|
||||
private const int MinChannel = 0;
|
||||
private const int MaxChannel = 10000;
|
||||
|
||||
private float range;
|
||||
|
||||
private int channel;
|
||||
@@ -49,7 +52,7 @@ namespace Barotrauma.Items.Components
|
||||
get { return channel; }
|
||||
set
|
||||
{
|
||||
channel = MathHelper.Clamp(value, 0, 10000);
|
||||
channel = MathHelper.Clamp(value, MinChannel, MaxChannel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,7 +298,14 @@ namespace Barotrauma.Items.Components
|
||||
case "set_channel":
|
||||
if (int.TryParse(signal.value, out int newChannel))
|
||||
{
|
||||
int prevChannel = Channel;
|
||||
Channel = newChannel;
|
||||
if (prevChannel != Channel)
|
||||
{
|
||||
#if SERVER
|
||||
item.CreateServerEvent(this);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "set_range":
|
||||
|
||||
@@ -20,7 +20,11 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
string signalOut = sendOutput == 1 ? output : falseOutput;
|
||||
if (string.IsNullOrEmpty(signalOut)) { return; }
|
||||
if (string.IsNullOrEmpty(signalOut))
|
||||
{
|
||||
IsActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
item.SendSignal(new Signal(signalOut, sender: signalSender[0] ?? signalSender[1]), "signal_out");
|
||||
}
|
||||
|
||||
@@ -1442,6 +1442,11 @@ namespace Barotrauma.Items.Components
|
||||
crosshairPointerSprite?.Remove(); crosshairPointerSprite = null;
|
||||
moveSoundChannel?.Dispose(); moveSoundChannel = null;
|
||||
WeaponIndicatorSprite?.Remove(); WeaponIndicatorSprite = null;
|
||||
if (powerIndicator != null)
|
||||
{
|
||||
powerIndicator.RectTransform.Parent = null;
|
||||
powerIndicator = null;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -479,7 +479,17 @@ namespace Barotrauma
|
||||
{
|
||||
if (i < 0 || i >= slots.Length)
|
||||
{
|
||||
string errorMsg = $"Inventory.TryPutItem failed: index was out of range (item: {(item?.Name ?? "null")}, inventory: {(Owner?.ToString() ?? "null")}).\n" + Environment.StackTrace.CleanupStackTrace();
|
||||
string thisItemStr = item?.prefab.Identifier ?? "null";
|
||||
string ownerStr = "null";
|
||||
if (Owner is Item ownerItem)
|
||||
{
|
||||
ownerStr = ownerItem.prefab.Identifier;
|
||||
}
|
||||
else if (Owner is Character ownerCharacter)
|
||||
{
|
||||
ownerStr = ownerCharacter.SpeciesName;
|
||||
}
|
||||
string errorMsg = $"Inventory.TryPutItem failed: index was out of range (item: {thisItemStr}, inventory: {ownerStr}).";
|
||||
GameAnalyticsManager.AddErrorEventOnce("Inventory.TryPutItem:IndexOutOfRange", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
|
||||
@@ -1741,7 +1741,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
if (updateableComponents.Count == 0 && aiTarget == null && !hasStatusEffectsOfType[(int)ActionType.Always] && (body == null || !body.Enabled))
|
||||
if (updateableComponents.Count == 0 && !hasStatusEffectsOfType[(int)ActionType.Always] && (body == null || !body.Enabled))
|
||||
{
|
||||
#if CLIENT
|
||||
positionBuffer.Clear();
|
||||
@@ -2088,19 +2088,18 @@ namespace Barotrauma
|
||||
return controller != null;
|
||||
}
|
||||
|
||||
public bool SendSignal(string signal, string connectionName)
|
||||
public void SendSignal(string signal, string connectionName)
|
||||
{
|
||||
return SendSignal(new Signal(signal), connectionName);
|
||||
SendSignal(new Signal(signal), connectionName);
|
||||
}
|
||||
|
||||
public bool SendSignal(Signal signal, string connectionName)
|
||||
public void SendSignal(Signal signal, string connectionName)
|
||||
{
|
||||
if (connections == null) { return false; }
|
||||
if (!connections.TryGetValue(connectionName, out Connection connection)) { return false; }
|
||||
if (connections == null) { return; }
|
||||
if (!connections.TryGetValue(connectionName, out Connection connection)) { return; }
|
||||
|
||||
signal.source ??= this;
|
||||
SendSignal(signal, connection);
|
||||
return true;
|
||||
}
|
||||
|
||||
private readonly HashSet<(Signal Signal, Connection Connection)> delayedSignals = new HashSet<(Signal Signal, Connection Connection)>();
|
||||
@@ -3248,6 +3247,9 @@ namespace Barotrauma
|
||||
foreach (ItemComponent ic in components)
|
||||
{
|
||||
ic.Remove();
|
||||
#if CLIENT
|
||||
ic.GuiFrame = null;
|
||||
#endif
|
||||
}
|
||||
ItemList.Remove(this);
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ namespace Barotrauma.MapCreatures.Behavior
|
||||
public readonly BallastFloraBehavior? ParentBallastFlora;
|
||||
public int ID = -1;
|
||||
|
||||
public ushort ClaimedItem;
|
||||
public bool HasClaimedItem;
|
||||
public Item ClaimedItem;
|
||||
public int ClaimedItemId = -1;
|
||||
|
||||
public float MaxHealth = 100f;
|
||||
public float Health = 100f;
|
||||
@@ -271,10 +271,29 @@ namespace Barotrauma.MapCreatures.Behavior
|
||||
{
|
||||
ClaimTarget(item, Branches.FirstOrDefault(b => b.ID == branchid), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
string errorMsg = $"Error in BallastFloraBehavior.OnMapLoaded: could not find the item claimed by the ballast flora.";
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("BallastFloraBehavior.OnMapLoaded:ClaimedItemNotFound", GameAnalyticsManager.ErrorSeverity.Warning, errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (BallastFloraBranch branch in Branches)
|
||||
{
|
||||
if (branch.ClaimedItemId > -1)
|
||||
{
|
||||
if (Entity.FindEntityByID((ushort)branch.ClaimedItemId) is Item item)
|
||||
{
|
||||
branch.ClaimedItem = item;
|
||||
}
|
||||
else
|
||||
{
|
||||
string errorMsg = $"Error in BallastFloraBehavior.OnMapLoaded: could not find the item claimed by a branch.";
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("BallastFloraBehavior.OnMapLoaded:BranchClaimedItemNotFound", GameAnalyticsManager.ErrorSeverity.Warning, errorMsg);
|
||||
}
|
||||
}
|
||||
UpdateConnections(branch);
|
||||
CreateBody(branch);
|
||||
}
|
||||
@@ -335,9 +354,9 @@ namespace Barotrauma.MapCreatures.Behavior
|
||||
new XAttribute("sides", (int)branch.Sides),
|
||||
new XAttribute("blockedsides", (int)branch.BlockedSides));
|
||||
|
||||
if (branch.HasClaimedItem)
|
||||
if (branch.ClaimedItem != null)
|
||||
{
|
||||
be.Add(new XAttribute("claimed", (int)branch.ClaimedItem));
|
||||
be.Add(new XAttribute("claimed", (int)(branch.ClaimedItem?.ID ?? -1)));
|
||||
}
|
||||
|
||||
saveElement.Add(be);
|
||||
@@ -345,6 +364,13 @@ namespace Barotrauma.MapCreatures.Behavior
|
||||
|
||||
foreach (Item target in ClaimedTargets)
|
||||
{
|
||||
if (target.Infector == null)
|
||||
{
|
||||
string errorMsg = $"Error in BallastFloraBehavior.Save: claimed target \"{target.Prefab.Identifier}\" had no infector set.";
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("BallastFloraBehavior.Save:InfectorNull", GameAnalyticsManager.ErrorSeverity.Warning, errorMsg);
|
||||
continue;
|
||||
}
|
||||
XElement te = new XElement("ClaimedTarget", new XAttribute("id", target.ID), new XAttribute("branchId", target.Infector.ID));
|
||||
saveElement.Add(te);
|
||||
}
|
||||
@@ -352,7 +378,7 @@ namespace Barotrauma.MapCreatures.Behavior
|
||||
element.Add(saveElement);
|
||||
}
|
||||
|
||||
public void LoadSave(XElement element)
|
||||
public void LoadSave(XElement element, IdRemap idRemap)
|
||||
{
|
||||
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
|
||||
Offset = element.GetAttributeVector2("offset", Vector2.Zero);
|
||||
@@ -361,21 +387,20 @@ namespace Barotrauma.MapCreatures.Behavior
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "branch":
|
||||
LoadBranch(subElement);
|
||||
LoadBranch(subElement, idRemap);
|
||||
break;
|
||||
|
||||
case "claimedtarget":
|
||||
int id = subElement.GetAttributeInt("id", -1);
|
||||
int branchId = subElement.GetAttributeInt("branchId", -1);
|
||||
if (id > 0)
|
||||
{
|
||||
tempClaimedTargets.Add(Tuple.Create((UInt16)id, branchId));
|
||||
tempClaimedTargets.Add(Tuple.Create(idRemap.GetOffsetId(id), branchId));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void LoadBranch(XElement branchElement)
|
||||
void LoadBranch(XElement branchElement, IdRemap idRemap)
|
||||
{
|
||||
Vector2 pos = branchElement.GetAttributeVector2("pos", Vector2.Zero);
|
||||
bool isRoot = branchElement.GetAttributeBool("isroot", false);
|
||||
@@ -400,8 +425,7 @@ namespace Barotrauma.MapCreatures.Behavior
|
||||
|
||||
if (claimedId > -1)
|
||||
{
|
||||
newBranch.HasClaimedItem = true;
|
||||
newBranch.ClaimedItem = (ushort) claimedId;
|
||||
newBranch.ClaimedItemId = idRemap.GetOffsetId((ushort)claimedId);
|
||||
}
|
||||
|
||||
Branches.Add(newBranch);
|
||||
@@ -767,8 +791,7 @@ namespace Barotrauma.MapCreatures.Behavior
|
||||
|
||||
if (branch != null)
|
||||
{
|
||||
branch.ClaimedItem = target.ID;
|
||||
branch.HasClaimedItem = true;
|
||||
branch.ClaimedItem = target;
|
||||
}
|
||||
|
||||
#if SERVER
|
||||
@@ -977,7 +1000,7 @@ namespace Barotrauma.MapCreatures.Behavior
|
||||
|
||||
if (isClient) { return; }
|
||||
|
||||
if (branch.HasClaimedItem)
|
||||
if (branch.ClaimedItem != null)
|
||||
{
|
||||
RemoveClaim(branch.ClaimedItem);
|
||||
}
|
||||
@@ -995,41 +1018,34 @@ namespace Barotrauma.MapCreatures.Behavior
|
||||
#endif
|
||||
}
|
||||
|
||||
public void RemoveClaim(ushort id)
|
||||
public void RemoveClaim(Item item)
|
||||
{
|
||||
ClaimedTargets.ForEachMod(item =>
|
||||
if (!IgnoredTargets.ContainsKey(item))
|
||||
{
|
||||
if (item.ID == id)
|
||||
IgnoredTargets.Add(item, 10);
|
||||
}
|
||||
|
||||
ClaimedTargets.Remove(item);
|
||||
item.Infector = null;
|
||||
|
||||
ClaimedJunctionBoxes.ForEachMod(jb =>
|
||||
{
|
||||
if (jb.Item == item)
|
||||
{
|
||||
if (!IgnoredTargets.ContainsKey(item))
|
||||
{
|
||||
IgnoredTargets.Add(item, 10);
|
||||
}
|
||||
|
||||
ClaimedTargets.Remove(item);
|
||||
item.Infector = null;
|
||||
|
||||
ClaimedJunctionBoxes.ForEachMod(jb =>
|
||||
{
|
||||
if (jb.Item == item)
|
||||
{
|
||||
ClaimedJunctionBoxes.Remove(jb);
|
||||
}
|
||||
});
|
||||
|
||||
ClaimedBatteries.ForEachMod(bat =>
|
||||
{
|
||||
if (bat.Item == item)
|
||||
{
|
||||
ClaimedBatteries.Remove(bat);
|
||||
}
|
||||
});
|
||||
|
||||
#if SERVER
|
||||
SendNetworkMessage(this, NetworkHeader.Infect, item.ID, false);
|
||||
#endif
|
||||
ClaimedJunctionBoxes.Remove(jb);
|
||||
}
|
||||
});
|
||||
|
||||
ClaimedBatteries.ForEachMod(bat =>
|
||||
{
|
||||
if (bat.Item == item)
|
||||
{
|
||||
ClaimedBatteries.Remove(bat);
|
||||
}
|
||||
});
|
||||
#if SERVER
|
||||
SendNetworkMessage(this, NetworkHeader.Infect, item.ID, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Kill()
|
||||
|
||||
@@ -1540,7 +1540,7 @@ namespace Barotrauma
|
||||
if (prefab != null)
|
||||
{
|
||||
hull.BallastFlora = new BallastFloraBehavior(hull, prefab, Vector2.Zero);
|
||||
hull.BallastFlora.LoadSave(subElement);
|
||||
hull.BallastFlora.LoadSave(subElement, idRemap);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -205,6 +205,8 @@ namespace Barotrauma
|
||||
foreach (var cell in Cells)
|
||||
{
|
||||
cell.CellType = CellType.Removed;
|
||||
cell.OnDestroyed?.Invoke();
|
||||
cell.OnDestroyed = null;
|
||||
}
|
||||
GameMain.World.Remove(Body);
|
||||
Dispose();
|
||||
|
||||
@@ -25,7 +25,17 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public const int MaxSubmarineWidth = 16000;
|
||||
|
||||
public static Level Loaded { get; private set; }
|
||||
private static Level loaded;
|
||||
public static Level Loaded
|
||||
{
|
||||
get { return loaded; }
|
||||
private set
|
||||
{
|
||||
if (loaded == value) { return; }
|
||||
loaded = value;
|
||||
GameAnalyticsManager.SetCurrentLevel(loaded?.LevelData);
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum PositionType
|
||||
@@ -578,8 +588,8 @@ namespace Barotrauma
|
||||
{
|
||||
for (int y = siteInterval.Y / 2; y < borders.Height - siteInterval.Y / 2; y += siteInterval.Y)
|
||||
{
|
||||
int siteX = x + Rand.Range(-siteVariance.X, siteVariance.X, Rand.RandSync.Server);
|
||||
int siteY = y + Rand.Range(-siteVariance.Y, siteVariance.Y, Rand.RandSync.Server);
|
||||
int siteX = x + Rand.Range(-siteVariance.X, siteVariance.X + 1, Rand.RandSync.Server);
|
||||
int siteY = y + Rand.Range(-siteVariance.Y, siteVariance.Y + 1, Rand.RandSync.Server);
|
||||
|
||||
bool closeToTunnel = false;
|
||||
bool closeToCave = false;
|
||||
@@ -1776,12 +1786,12 @@ namespace Barotrauma
|
||||
new Point(0, BottomPos)
|
||||
};
|
||||
|
||||
int mountainCount = Rand.Range(GenerationParams.MountainCountMin, GenerationParams.MountainCountMax, Rand.RandSync.Server);
|
||||
int mountainCount = Rand.Range(GenerationParams.MountainCountMin, GenerationParams.MountainCountMax + 1, Rand.RandSync.Server);
|
||||
for (int i = 0; i < mountainCount; i++)
|
||||
{
|
||||
bottomPositions.Add(
|
||||
new Point(Size.X / (mountainCount + 1) * (i + 1),
|
||||
BottomPos + Rand.Range(GenerationParams.MountainHeightMin, GenerationParams.MountainHeightMax, Rand.RandSync.Server)));
|
||||
BottomPos + Rand.Range(GenerationParams.MountainHeightMin, GenerationParams.MountainHeightMax + 1, Rand.RandSync.Server)));
|
||||
}
|
||||
bottomPositions.Add(new Point(Size.X, BottomPos));
|
||||
|
||||
@@ -1794,7 +1804,7 @@ namespace Barotrauma
|
||||
bottomPositions.Insert(i + 1,
|
||||
new Point(
|
||||
(bottomPositions[i].X + bottomPositions[i + 1].X) / 2,
|
||||
(bottomPositions[i].Y + bottomPositions[i + 1].Y) / 2 + Rand.Range(0, GenerationParams.SeaFloorVariance, Rand.RandSync.Server)));
|
||||
(bottomPositions[i].Y + bottomPositions[i + 1].Y) / 2 + Rand.Range(0, GenerationParams.SeaFloorVariance + 1, Rand.RandSync.Server)));
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -1881,7 +1891,7 @@ namespace Barotrauma
|
||||
Tunnels.Add(tunnel);
|
||||
caveBranches.Add(tunnel);
|
||||
|
||||
int branches = Rand.Range(caveParams.MinBranchCount, caveParams.MaxBranchCount, Rand.RandSync.Server);
|
||||
int branches = Rand.Range(caveParams.MinBranchCount, caveParams.MaxBranchCount + 1, Rand.RandSync.Server);
|
||||
for (int j = 0; j < branches; j++)
|
||||
{
|
||||
Tunnel parentBranch = caveBranches.GetRandom(Rand.RandSync.Server);
|
||||
@@ -2441,7 +2451,7 @@ namespace Barotrauma
|
||||
}, randSync: Rand.RandSync.Server);
|
||||
|
||||
if (location.Cell == null || location.Edge == null) { break; }
|
||||
int clusterSize = Rand.Range(GenerationParams.ResourceClusterSizeRange.X, GenerationParams.ResourceClusterSizeRange.Y, Rand.RandSync.Server);
|
||||
int clusterSize = Rand.Range(GenerationParams.ResourceClusterSizeRange.X, GenerationParams.ResourceClusterSizeRange.Y + 1, Rand.RandSync.Server);
|
||||
PlaceResources(itemPrefab, clusterSize, location, out var abyssResources);
|
||||
var abyssClusterLocation = new ClusterLocation(location.Cell, location.Edge, initializeResourceList: true);
|
||||
abyssClusterLocation.Resources.AddRange(abyssResources);
|
||||
@@ -3310,13 +3320,6 @@ namespace Barotrauma
|
||||
return pathCells;
|
||||
}
|
||||
|
||||
public string GetWreckIDTag(string originalTag, Submarine wreck)
|
||||
{
|
||||
string shortSeed = ToolBox.StringToInt(LevelData.Seed + wreck?.Info.Name).ToString();
|
||||
if (shortSeed.Length > 6) { shortSeed = shortSeed.Substring(0, 6); }
|
||||
return originalTag + "_" + shortSeed;
|
||||
}
|
||||
|
||||
public bool IsCloseToStart(Vector2 position, float minDist) => IsCloseToStart(position.ToPoint(), minDist);
|
||||
public bool IsCloseToEnd(Vector2 position, float minDist) => IsCloseToEnd(position.ToPoint(), minDist);
|
||||
|
||||
@@ -4058,7 +4061,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (Submarine wreck in Wrecks)
|
||||
{
|
||||
int corpseCount = Rand.Range(Loaded.GenerationParams.MinCorpseCount, Loaded.GenerationParams.MaxCorpseCount);
|
||||
int corpseCount = Rand.Range(Loaded.GenerationParams.MinCorpseCount, Loaded.GenerationParams.MaxCorpseCount + 1);
|
||||
var allSpawnPoints = WayPoint.WayPointList.FindAll(wp => wp.Submarine == wreck && wp.CurrentHull != null);
|
||||
var pathPoints = allSpawnPoints.FindAll(wp => wp.SpawnType == SpawnType.Path);
|
||||
pathPoints.Shuffle(Rand.RandSync.Unsynced);
|
||||
@@ -4115,6 +4118,7 @@ namespace Barotrauma
|
||||
corpse.EnableDespawn = false;
|
||||
selectedPrefab.GiveItems(corpse, wreck);
|
||||
corpse.Kill(CauseOfDeathType.Unknown, causeOfDeathAffliction: null, log: false);
|
||||
corpse.GiveIdCardTags(sp);
|
||||
spawnCounter++;
|
||||
|
||||
static CorpsePrefab GetCorpsePrefab(Func<CorpsePrefab, bool> predicate)
|
||||
|
||||
+1
-1
@@ -304,7 +304,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (LevelObjectPrefab.ChildObject child in prefab.ChildObjects)
|
||||
{
|
||||
int childCount = Rand.Range(child.MinCount, child.MaxCount, Rand.RandSync.Server);
|
||||
int childCount = Rand.Range(child.MinCount, child.MaxCount + 1, Rand.RandSync.Server);
|
||||
for (int j = 0; j < childCount; j++)
|
||||
{
|
||||
var matchingPrefabs = LevelObjectPrefab.List.Where(p => child.AllowedNames.Contains(p.Name));
|
||||
|
||||
@@ -436,7 +436,7 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Are there any active contacts between the physics body and the target entity
|
||||
/// </summary>
|
||||
public static bool CheckContactsForEntity(PhysicsBody triggerBody, Entity separatingEntity)
|
||||
public static bool CheckContactsForEntity(PhysicsBody triggerBody, Entity targetEntity)
|
||||
{
|
||||
foreach (Fixture fixture in triggerBody.FarseerBody.FixtureList)
|
||||
{
|
||||
@@ -447,10 +447,11 @@ namespace Barotrauma
|
||||
contactEdge.Contact.Enabled &&
|
||||
contactEdge.Contact.IsTouching)
|
||||
{
|
||||
if (contactEdge.Contact.FixtureA != fixture && contactEdge.Contact.FixtureB != fixture)
|
||||
{
|
||||
if (GetEntity(contactEdge.Contact.FixtureB) == separatingEntity || GetEntity(contactEdge.Contact.FixtureA) == separatingEntity) { return true; }
|
||||
}
|
||||
if ((contactEdge.Contact.FixtureA.Body == triggerBody.FarseerBody && GetEntity(contactEdge.Contact.FixtureB) == targetEntity) ||
|
||||
(contactEdge.Contact.FixtureB.Body == triggerBody.FarseerBody && GetEntity(contactEdge.Contact.FixtureA) == targetEntity))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
contactEdge = contactEdge.Next;
|
||||
}
|
||||
@@ -560,6 +561,8 @@ namespace Barotrauma
|
||||
|
||||
foreach (Entity triggerer in triggerers)
|
||||
{
|
||||
if (triggerer.Removed) { continue; }
|
||||
|
||||
ApplyStatusEffects(statusEffects, worldPosition, triggerer, deltaTime, targets);
|
||||
|
||||
if (triggerer is IDamageable damageable)
|
||||
@@ -691,6 +694,8 @@ namespace Barotrauma
|
||||
|
||||
private void ApplyForce(PhysicsBody body)
|
||||
{
|
||||
if (body == null) { return; }
|
||||
|
||||
float distFactor = 1.0f;
|
||||
if (ForceFalloff)
|
||||
{
|
||||
|
||||
@@ -352,6 +352,7 @@ namespace Barotrauma
|
||||
if (hull.Submarine != sub) { continue; }
|
||||
hull.WaterVolume = 0.0f;
|
||||
hull.OxygenPercentage = 100.0f;
|
||||
hull.BallastFlora?.Kill();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -782,7 +782,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (priceInfo.MaxAvailableAmount > priceInfo.MinAvailableAmount)
|
||||
{
|
||||
quantity = Rand.Range(priceInfo.MinAvailableAmount, priceInfo.MaxAvailableAmount);
|
||||
quantity = Rand.Range(priceInfo.MinAvailableAmount, priceInfo.MaxAvailableAmount + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1010,7 +1010,7 @@ namespace Barotrauma
|
||||
|
||||
private void GenerateRandomPriceModifier()
|
||||
{
|
||||
StorePriceModifier = Rand.Range(-StorePriceModifierRange, StorePriceModifierRange);
|
||||
StorePriceModifier = Rand.Range(-StorePriceModifierRange, StorePriceModifierRange + 1);
|
||||
}
|
||||
|
||||
private void CreateStoreSpecials()
|
||||
|
||||
@@ -112,7 +112,7 @@ namespace Barotrauma
|
||||
System.Diagnostics.Debug.Assert(!Locations.Contains(null));
|
||||
for (int i = 0; i < Locations.Count; i++)
|
||||
{
|
||||
Locations[i].Reputation ??= new Reputation(campaign.CampaignMetadata, $"location.{i}", -100, 100, Rand.Range(-10, 10, Rand.RandSync.Server));
|
||||
Locations[i].Reputation ??= new Reputation(campaign.CampaignMetadata, Locations[i], $"location.{i}", -100, 100, Rand.Range(-10, 11, Rand.RandSync.Server));
|
||||
}
|
||||
|
||||
List<XElement> connectionElements = new List<XElement>();
|
||||
@@ -214,7 +214,7 @@ namespace Barotrauma
|
||||
|
||||
for (int i = 0; i < Locations.Count; i++)
|
||||
{
|
||||
Locations[i].Reputation ??= new Reputation(campaign.CampaignMetadata, $"location.{i}", -100, 100, Rand.Range(-10, 10, Rand.RandSync.Server));
|
||||
Locations[i].Reputation ??= new Reputation(campaign.CampaignMetadata, Locations[i], $"location.{i}", -100, 100, Rand.Range(-10, 11, Rand.RandSync.Server));
|
||||
}
|
||||
|
||||
foreach (Location location in Locations)
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace Barotrauma
|
||||
|
||||
private float? maxHealth;
|
||||
|
||||
[Serialize(100.0f, true), Editable]
|
||||
[Serialize(100.0f, true), Editable(MinValueFloat = 0)]
|
||||
public float MaxHealth
|
||||
{
|
||||
get => maxHealth ?? Prefab.Health;
|
||||
@@ -898,8 +898,8 @@ namespace Barotrauma
|
||||
{
|
||||
var worldRect = section.WorldRect;
|
||||
Vector2 particlePos = new Vector2(
|
||||
Rand.Range(worldRect.X, worldRect.Right),
|
||||
Rand.Range(worldRect.Y - worldRect.Height, worldRect.Y));
|
||||
Rand.Range(worldRect.X, worldRect.Right + 1),
|
||||
Rand.Range(worldRect.Y - worldRect.Height, worldRect.Y + 1));
|
||||
|
||||
var particle = GameMain.ParticleManager.CreateParticle("shrapnel", particlePos, Rand.Vector(Rand.Range(1.0f, 50.0f)), collisionIgnoreTimer: 1f);
|
||||
if (particle == null) break;
|
||||
|
||||
@@ -79,9 +79,19 @@ namespace Barotrauma.Networking
|
||||
|
||||
public readonly string SenderName;
|
||||
|
||||
private Color? customTextColor;
|
||||
public Color Color
|
||||
{
|
||||
get { return MessageColor[(int)Type]; }
|
||||
get
|
||||
{
|
||||
if (customTextColor != null) { return customTextColor.Value; }
|
||||
return MessageColor[(int)Type];
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
customTextColor = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetTimeStamp()
|
||||
@@ -105,7 +115,7 @@ namespace Barotrauma.Networking
|
||||
set;
|
||||
}
|
||||
|
||||
protected ChatMessage(string senderName, string text, ChatMessageType type, Character sender, Client client, PlayerConnectionChangeType changeType = PlayerConnectionChangeType.None)
|
||||
protected ChatMessage(string senderName, string text, ChatMessageType type, Character sender, Client client, PlayerConnectionChangeType changeType = PlayerConnectionChangeType.None, Color? textColor = null)
|
||||
{
|
||||
Text = text;
|
||||
Type = type;
|
||||
@@ -115,11 +125,13 @@ namespace Barotrauma.Networking
|
||||
|
||||
SenderName = senderName;
|
||||
ChangeType = changeType;
|
||||
}
|
||||
|
||||
public static ChatMessage Create(string senderName, string text, ChatMessageType type, Character sender, Client client = null, PlayerConnectionChangeType changeType = PlayerConnectionChangeType.None)
|
||||
customTextColor = textColor;
|
||||
}
|
||||
|
||||
public static ChatMessage Create(string senderName, string text, ChatMessageType type, Character sender, Client client = null, PlayerConnectionChangeType changeType = PlayerConnectionChangeType.None, Color? textColor = null)
|
||||
{
|
||||
return new ChatMessage(senderName, text, type, sender, client ?? GameMain.NetworkMember?.ConnectedClients?.Find(c => c.Character != null && c.Character == sender), changeType);
|
||||
return new ChatMessage(senderName, text, type, sender, client ?? GameMain.NetworkMember?.ConnectedClients?.Find(c => c.Character != null && c.Character == sender), changeType, textColor);
|
||||
}
|
||||
|
||||
public static string GetChatMessageCommand(string message, out string messageWithoutCommand)
|
||||
|
||||
@@ -234,24 +234,24 @@ namespace Barotrauma.Networking
|
||||
|
||||
var radio = sender.Inventory.AllItems.FirstOrDefault(i => i.GetComponent<WifiComponent>() != null);
|
||||
if (radio == null || !sender.HasEquippedItem(radio)) { return false; }
|
||||
|
||||
|
||||
var radioComponent = radio.GetComponent<WifiComponent>();
|
||||
if (radioComponent == null) { return false; }
|
||||
return radioComponent.HasRequiredContainedItems(sender, addMessage: false);
|
||||
}
|
||||
|
||||
public void AddChatMessage(string message, ChatMessageType type, string senderName = "", Client senderClient = null, Character senderCharacter = null, PlayerConnectionChangeType changeType = PlayerConnectionChangeType.None)
|
||||
public void AddChatMessage(string message, ChatMessageType type, string senderName = "", Client senderClient = null, Character senderCharacter = null, PlayerConnectionChangeType changeType = PlayerConnectionChangeType.None, Color? textColor = null)
|
||||
{
|
||||
AddChatMessage(ChatMessage.Create(senderName, message, type, senderCharacter, senderClient, changeType: changeType));
|
||||
AddChatMessage(ChatMessage.Create(senderName, message, type, senderCharacter, senderClient, changeType: changeType, textColor: textColor));
|
||||
}
|
||||
|
||||
public virtual void AddChatMessage(ChatMessage message)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message.Text)) { return; }
|
||||
|
||||
|
||||
if (message.Sender != null && !message.Sender.IsDead)
|
||||
{
|
||||
message.Sender.ShowSpeechBubble(2.0f, ChatMessage.MessageColor[(int)message.Type]);
|
||||
message.Sender.ShowSpeechBubble(2.0f, message.Color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -124,7 +124,6 @@ namespace Barotrauma.Steam
|
||||
return unlocked;
|
||||
}
|
||||
|
||||
|
||||
public static bool IncrementStat(string statName, int increment)
|
||||
{
|
||||
if (!isInitialized || !Steamworks.SteamClient.IsValid) { return false; }
|
||||
@@ -161,6 +160,12 @@ namespace Barotrauma.Steam
|
||||
return success;
|
||||
}
|
||||
|
||||
public static int GetStatInt(string statName)
|
||||
{
|
||||
if (!isInitialized || !Steamworks.SteamClient.IsValid) { return 0; }
|
||||
return Steamworks.SteamUserStats.GetStatInt(statName);
|
||||
}
|
||||
|
||||
public static bool StoreStats()
|
||||
{
|
||||
if (!isInitialized || !Steamworks.SteamClient.IsValid) { return false; }
|
||||
@@ -175,6 +180,17 @@ namespace Barotrauma.Steam
|
||||
return success;
|
||||
}
|
||||
|
||||
public static bool TryGetUnlockedAchievements(out List<Steamworks.Data.Achievement> achievements)
|
||||
{
|
||||
if (!isInitialized || !Steamworks.SteamClient.IsValid)
|
||||
{
|
||||
achievements = null;
|
||||
return false;
|
||||
}
|
||||
achievements = Steamworks.SteamUserStats.Achievements.Where(a => a.State).ToList();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void Update(float deltaTime)
|
||||
{
|
||||
if (!isInitialized) { return; }
|
||||
|
||||
+6
-14
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
@@ -7,20 +6,18 @@ namespace Barotrauma
|
||||
{
|
||||
public class PerformanceCounter
|
||||
{
|
||||
public long TotalFrames { get; private set; }
|
||||
public double TotalSeconds { get; private set; }
|
||||
public double AverageFramesPerSecond { get; private set; }
|
||||
public double CurrentFramesPerSecond { get; private set; }
|
||||
|
||||
public const int MaximumSamples = 10;
|
||||
|
||||
private Queue<double> sampleBuffer = new Queue<double>();
|
||||
private readonly Queue<double> sampleBuffer = new Queue<double>();
|
||||
|
||||
private Dictionary<string, Queue<long>> elapsedTicks = new Dictionary<string, Queue<long>>();
|
||||
private Dictionary<string, long> avgTicksPerFrame = new Dictionary<string, long>();
|
||||
private readonly Dictionary<string, Queue<long>> elapsedTicks = new Dictionary<string, Queue<long>>();
|
||||
private readonly Dictionary<string, long> avgTicksPerFrame = new Dictionary<string, long>();
|
||||
|
||||
#if CLIENT
|
||||
internal Graph UpdateTimeGraph = new Graph(500), UpdateIterationsGraph = new Graph(500), DrawTimeGraph = new Graph(500);
|
||||
internal Graph UpdateTimeGraph = new Graph(500), DrawTimeGraph = new Graph(500);
|
||||
#endif
|
||||
|
||||
public IEnumerable<string> GetSavedIdentifiers
|
||||
@@ -50,7 +47,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (deltaTime == 0.0f) { return false; }
|
||||
|
||||
CurrentFramesPerSecond = (1.0 / deltaTime);
|
||||
CurrentFramesPerSecond = 1.0 / deltaTime;
|
||||
|
||||
sampleBuffer.Enqueue(CurrentFramesPerSecond);
|
||||
|
||||
@@ -64,12 +61,7 @@ namespace Barotrauma
|
||||
AverageFramesPerSecond = CurrentFramesPerSecond;
|
||||
}
|
||||
|
||||
if (AverageFramesPerSecond < 0 || AverageFramesPerSecond > 500) { }
|
||||
|
||||
TotalFrames++;
|
||||
TotalSeconds += deltaTime;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,7 @@
|
||||
using Barotrauma;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
@@ -157,6 +158,11 @@ namespace Voronoi2
|
||||
public bool IsDestructible;
|
||||
public bool DoesDamage;
|
||||
|
||||
/// <summary>
|
||||
/// Executed when the cell is destroyed (only applies to destructible level walls)
|
||||
/// </summary>
|
||||
public Action OnDestroyed;
|
||||
|
||||
public Vector2 Center
|
||||
{
|
||||
get { return new Vector2((float)Site.Coord.X, (float)Site.Coord.Y) + Translation; }
|
||||
|
||||
@@ -556,7 +556,14 @@ namespace Barotrauma
|
||||
switch (Name)
|
||||
{
|
||||
case nameof(Powered.Voltage):
|
||||
if (parentObject is Powered powered) { value = powered.Voltage; return true; }
|
||||
{
|
||||
if (parentObject is Powered powered) { value = powered.Voltage; return true; }
|
||||
}
|
||||
break;
|
||||
case nameof(Powered.CurrPowerConsumption):
|
||||
{
|
||||
if (parentObject is Powered powered) { value = powered.CurrPowerConsumption; return true; }
|
||||
}
|
||||
break;
|
||||
case nameof(PowerContainer.Charge):
|
||||
{
|
||||
@@ -568,6 +575,11 @@ namespace Barotrauma
|
||||
if (parentObject is PowerContainer powerContainer) { value = powerContainer.ChargePercentage; return true; }
|
||||
}
|
||||
break;
|
||||
case nameof(PowerContainer.RechargeRatio):
|
||||
{
|
||||
if (parentObject is PowerContainer powerContainer) { value = powerContainer.RechargeRatio; return true; }
|
||||
}
|
||||
break;
|
||||
case nameof(Reactor.AvailableFuel):
|
||||
{ if (parentObject is Reactor reactor) { value = reactor.AvailableFuel; return true; } }
|
||||
break;
|
||||
|
||||
@@ -262,6 +262,28 @@ namespace Barotrauma
|
||||
return val;
|
||||
}
|
||||
|
||||
public static double GetAttributeDouble(this XElement element, string name, double defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) { return defaultValue; }
|
||||
|
||||
double val = defaultValue;
|
||||
try
|
||||
{
|
||||
string strVal = element.Attribute(name).Value;
|
||||
if (strVal.LastOrDefault() == 'f')
|
||||
{
|
||||
strVal = strVal.Substring(0, strVal.Length - 1);
|
||||
}
|
||||
val = double.Parse(strVal, CultureInfo.InvariantCulture);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in " + element + "!", e);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
public static float[] GetAttributeFloatArray(this XElement element, string name, float[] defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) { return defaultValue; }
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private static List<WeakReference<Sprite>> list = new List<WeakReference<Sprite>>();
|
||||
private readonly static List<WeakReference<Sprite>> list = new List<WeakReference<Sprite>>();
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the xml element from where the sprite was created. Can be null if the sprite was not defined in xml!
|
||||
|
||||
@@ -329,7 +329,7 @@ namespace Barotrauma
|
||||
|
||||
private bool MatchesTagCondition(ISerializableEntity target)
|
||||
{
|
||||
if (!(target is Item item)) { return false; }
|
||||
if (!(target is Item item)) { return Operator == OperatorType.NotEquals; }
|
||||
|
||||
int matches = 0;
|
||||
foreach (string tag in SplitAttributeValue)
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Barotrauma
|
||||
public static double Accumulator;
|
||||
public const int FixedUpdateRate = 60;
|
||||
public const double Step = 1.0 / FixedUpdateRate;
|
||||
public const double AccumulatorMax = 0.25f;
|
||||
|
||||
private static int frameLimit;
|
||||
/// <summary>
|
||||
|
||||
@@ -62,6 +62,9 @@ namespace Barotrauma
|
||||
return (sync == RandSync.Unsynced ? localRandom : (syncedRandom[(int)sync])).NextDouble() * (maximum - minimum) + minimum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Min inclusive, Max exclusive!
|
||||
/// </summary>
|
||||
public static int Range(int minimum, int maximum, RandSync sync = RandSync.Unsynced)
|
||||
{
|
||||
CheckRandThreadSafety(sync);
|
||||
|
||||
Reference in New Issue
Block a user