Merge branch 'dev' of https://github.com/Regalis11/Barotrauma into unstable
This commit is contained in:
@@ -8,7 +8,6 @@ using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -158,11 +157,11 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public readonly bool SpawnIfCantBeContained;
|
||||
public readonly float Impulse;
|
||||
public readonly float Rotation;
|
||||
public readonly float RotationRad;
|
||||
public readonly int Count;
|
||||
public readonly float Spread;
|
||||
public readonly SpawnRotationType RotationType;
|
||||
public readonly float AimSpread;
|
||||
public readonly float AimSpreadRad;
|
||||
public readonly bool Equip;
|
||||
|
||||
public readonly float Condition;
|
||||
@@ -198,14 +197,14 @@ namespace Barotrauma
|
||||
|
||||
SpawnIfInventoryFull = element.GetAttributeBool("spawnifinventoryfull", false);
|
||||
SpawnIfCantBeContained = element.GetAttributeBool("spawnifcantbecontained", true);
|
||||
Impulse = element.GetAttributeFloat("impulse", element.GetAttributeFloat("speed", 0.0f));
|
||||
Impulse = element.GetAttributeFloat("impulse", element.GetAttributeFloat("launchimpulse", element.GetAttributeFloat("speed", 0.0f)));
|
||||
|
||||
Condition = MathHelper.Clamp(element.GetAttributeFloat("condition", 1.0f), 0.0f, 1.0f);
|
||||
|
||||
Rotation = element.GetAttributeFloat("rotation", 0.0f);
|
||||
RotationRad = MathHelper.ToRadians(element.GetAttributeFloat("rotation", 0.0f));
|
||||
Count = element.GetAttributeInt("count", 1);
|
||||
Spread = element.GetAttributeFloat("spread", 0f);
|
||||
AimSpread = element.GetAttributeFloat("aimspread", 0f);
|
||||
AimSpreadRad = MathHelper.ToRadians(element.GetAttributeFloat("aimspread", 0f));
|
||||
Equip = element.GetAttributeBool("equip", false);
|
||||
|
||||
string spawnTypeStr = element.GetAttributeString("spawnposition", "This");
|
||||
@@ -213,7 +212,7 @@ namespace Barotrauma
|
||||
{
|
||||
DebugConsole.ThrowError("Error in StatusEffect config - \"" + spawnTypeStr + "\" is not a valid spawn position.");
|
||||
}
|
||||
string rotationTypeStr = element.GetAttributeString("rotationtype", Rotation != 0 ? "Fixed" : "Target");
|
||||
string rotationTypeStr = element.GetAttributeString("rotationtype", RotationRad != 0 ? "Fixed" : "Target");
|
||||
if (!Enum.TryParse(rotationTypeStr, ignoreCase: true, out RotationType))
|
||||
{
|
||||
DebugConsole.ThrowError("Error in StatusEffect config - \"" + rotationTypeStr + "\" is not a valid rotation type.");
|
||||
@@ -264,12 +263,36 @@ namespace Barotrauma
|
||||
public string Name => $"Character Spawn Info ({SpeciesName})";
|
||||
public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
public bool TransferBuffs { get; private set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
public bool TransferAfflictions { get; private set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
public bool TransferInventory { get; private set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.No)]
|
||||
public Identifier SpeciesName { get; private set; }
|
||||
|
||||
[Serialize(1, IsPropertySaveable.No)]
|
||||
public int Count { get; private set; }
|
||||
|
||||
[Serialize(0, IsPropertySaveable.No)]
|
||||
public int Stun { get; private set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.No)]
|
||||
public Identifier AfflictionOnSpawn { get; private set; }
|
||||
|
||||
[Serialize(1, IsPropertySaveable.No)]
|
||||
public int AfflictionStrength { get; private set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
public bool TransferControl { get; private set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
public bool RemovePreviousCharacter { get; private set; }
|
||||
|
||||
[Serialize(0f, IsPropertySaveable.No)]
|
||||
public float Spread { get; private set; }
|
||||
|
||||
@@ -1548,24 +1571,23 @@ namespace Barotrauma
|
||||
{
|
||||
Character targetCharacter = CharacterFromTarget(target);
|
||||
if (targetCharacter?.Info == null) { continue; }
|
||||
if (!TalentTree.JobTalentTrees.TryGet(targetCharacter.Info.Job.Prefab.Identifier, out TalentTree talentTree)) { continue; }
|
||||
// for the sake of technical simplicity, for now do not allow talents to be given if the character could unlock them in their talent tree as well
|
||||
IEnumerable<Identifier> disallowedTalents = talentTree.TalentSubTrees.SelectMany(s => s.TalentOptionStages.SelectMany(o => o.Talents.Select(t => t.Identifier)));
|
||||
if (!TalentTree.JobTalentTrees.TryGet(targetCharacter.Info.Job.Prefab.Identifier, out TalentTree characterTalentTree)) { continue; }
|
||||
|
||||
foreach (GiveTalentInfo giveTalentInfo in giveTalentInfos)
|
||||
{
|
||||
IEnumerable<Identifier> viableTalents = giveTalentInfo.TalentIdentifiers.Where(s => !targetCharacter.Info.UnlockedTalents.Contains(s) && !disallowedTalents.Contains(s));
|
||||
if (viableTalents.None()) { continue; }
|
||||
|
||||
if (giveTalentInfo.GiveRandom)
|
||||
{
|
||||
{
|
||||
// for the sake of technical simplicity, for now do not allow talents to be given if the character could unlock them in their talent tree as well
|
||||
IEnumerable<Identifier> viableTalents = giveTalentInfo.TalentIdentifiers.Where(id => !targetCharacter.Info.UnlockedTalents.Contains(id) && !characterTalentTree.AllTalentIdentifiers.Contains(id));
|
||||
if (viableTalents.None()) { continue; }
|
||||
targetCharacter.GiveTalent(viableTalents.GetRandomUnsynced(), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (Identifier talent in viableTalents)
|
||||
foreach (Identifier id in giveTalentInfo.TalentIdentifiers)
|
||||
{
|
||||
targetCharacter.GiveTalent(talent, true);
|
||||
if (targetCharacter.Info.UnlockedTalents.Contains(id) || characterTalentTree.AllTalentIdentifiers.Contains(id)) { continue; }
|
||||
targetCharacter.GiveTalent(id, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1629,6 +1651,64 @@ namespace Barotrauma
|
||||
{
|
||||
SwarmBehavior.CreateSwarm(characters.Cast<AICharacter>());
|
||||
}
|
||||
if (!characterSpawnInfo.AfflictionOnSpawn.IsEmpty)
|
||||
{
|
||||
if (!AfflictionPrefab.Prefabs.TryGet(characterSpawnInfo.AfflictionOnSpawn, out AfflictionPrefab afflictionPrefab))
|
||||
{
|
||||
DebugConsole.NewMessage($"Could not apply an affliction to the spawned character(s). No affliction with the identifier \"{characterSpawnInfo.AfflictionOnSpawn}\" found.", Color.Red);
|
||||
return;
|
||||
}
|
||||
newCharacter.CharacterHealth.ApplyAffliction(newCharacter.AnimController.MainLimb, afflictionPrefab.Instantiate(characterSpawnInfo.AfflictionStrength));
|
||||
}
|
||||
if (characterSpawnInfo.Stun > 0)
|
||||
{
|
||||
newCharacter.SetStun(characterSpawnInfo.Stun);
|
||||
}
|
||||
foreach (var target in targets)
|
||||
{
|
||||
if (!(target is Character character)) { continue; }
|
||||
if (characterSpawnInfo.TransferInventory && character.Inventory != null && newCharacter.Inventory != null)
|
||||
{
|
||||
if (character.Inventory.Capacity != newCharacter.Inventory.Capacity) { return; }
|
||||
for (int i = 0; i < character.Inventory.Capacity && i < newCharacter.Inventory.Capacity; i++)
|
||||
{
|
||||
character.Inventory.GetItemsAt(i).ForEachMod(item => newCharacter.Inventory.TryPutItem(item, i, allowSwapping: true, allowCombine: false, user: null));
|
||||
}
|
||||
}
|
||||
if (characterSpawnInfo.TransferBuffs || characterSpawnInfo.TransferAfflictions)
|
||||
{
|
||||
foreach (Affliction affliction in character.CharacterHealth.GetAllAfflictions())
|
||||
{
|
||||
if (!characterSpawnInfo.TransferAfflictions && characterSpawnInfo.TransferBuffs && affliction.Prefab.IsBuff)
|
||||
{
|
||||
newCharacter.CharacterHealth.ApplyAffliction(newCharacter.AnimController.MainLimb, affliction.Prefab.Instantiate(affliction.Strength));
|
||||
}
|
||||
if (characterSpawnInfo.TransferAfflictions)
|
||||
{
|
||||
newCharacter.CharacterHealth.ApplyAffliction(newCharacter.AnimController.MainLimb, affliction.Prefab.Instantiate(affliction.Strength));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i == characterSpawnInfo.Count) // Only perform the below actions if this is the last character being spawned.
|
||||
{
|
||||
if (characterSpawnInfo.TransferControl)
|
||||
{
|
||||
#if CLIENT
|
||||
if (Character.Controlled == target)
|
||||
{
|
||||
Character.Controlled = newCharacter;
|
||||
}
|
||||
#elif SERVER
|
||||
/*foreach (Client c in GameMain.Server.ConnectedClients)
|
||||
{
|
||||
if (c.Character != target) { continue; }
|
||||
GameMain.Server.SetClientCharacter(c, newCharacter);
|
||||
}*/
|
||||
#endif
|
||||
}
|
||||
if (characterSpawnInfo.RemovePreviousCharacter) { Entity.Spawner?.AddEntityToRemoveQueue(character); }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1656,92 +1736,101 @@ namespace Barotrauma
|
||||
case ItemSpawnInfo.SpawnPositionType.This:
|
||||
Entity.Spawner.AddItemToSpawnQueue(chosenItemSpawnInfo.ItemPrefab, position + Rand.Vector(chosenItemSpawnInfo.Spread, Rand.RandSync.Unsynced), onSpawned: newItem =>
|
||||
{
|
||||
Item parentItem = entity as Item;
|
||||
Projectile projectile = newItem.GetComponent<Projectile>();
|
||||
if (projectile != null && user != null && sourceBody != null && entity != null)
|
||||
if (entity != null)
|
||||
{
|
||||
var rope = newItem.GetComponent<Rope>();
|
||||
if (rope != null && sourceBody.UserData is Limb sourceLimb)
|
||||
if (rope != null && sourceBody != null && sourceBody.UserData is Limb sourceLimb)
|
||||
{
|
||||
rope.Attach(sourceLimb, newItem);
|
||||
#if SERVER
|
||||
newItem.CreateServerEvent(rope);
|
||||
#endif
|
||||
}
|
||||
float spread = MathHelper.ToRadians(Rand.Range(-chosenItemSpawnInfo.AimSpread, chosenItemSpawnInfo.AimSpread));
|
||||
var worldPos = sourceBody.Position;
|
||||
float rotation = 0;
|
||||
if (user.Submarine != null)
|
||||
float spread = Rand.Range(-chosenItemSpawnInfo.AimSpreadRad, chosenItemSpawnInfo.AimSpreadRad);
|
||||
float rotation = chosenItemSpawnInfo.RotationRad;
|
||||
Vector2 worldPos;
|
||||
if (sourceBody != null)
|
||||
{
|
||||
worldPos += user.Submarine.Position;
|
||||
worldPos = sourceBody.Position;
|
||||
if (user?.Submarine != null)
|
||||
{
|
||||
worldPos += user.Submarine.Position;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
worldPos = entity.WorldPosition;
|
||||
}
|
||||
switch (chosenItemSpawnInfo.RotationType)
|
||||
{
|
||||
case ItemSpawnInfo.SpawnRotationType.Fixed:
|
||||
rotation = sourceBody.TransformRotation(chosenItemSpawnInfo.Rotation);
|
||||
if (sourceBody != null)
|
||||
{
|
||||
rotation = sourceBody.TransformRotation(chosenItemSpawnInfo.RotationRad);
|
||||
}
|
||||
else if (parentItem?.body != null)
|
||||
{
|
||||
rotation = parentItem.body.TransformRotation(chosenItemSpawnInfo.RotationRad);
|
||||
}
|
||||
break;
|
||||
case ItemSpawnInfo.SpawnRotationType.Target:
|
||||
rotation = MathUtils.VectorToAngle(entity.WorldPosition - worldPos);
|
||||
break;
|
||||
case ItemSpawnInfo.SpawnRotationType.Limb:
|
||||
rotation = sourceBody.TransformedRotation;
|
||||
if (sourceBody != null)
|
||||
{
|
||||
rotation = sourceBody.TransformedRotation;
|
||||
}
|
||||
break;
|
||||
case ItemSpawnInfo.SpawnRotationType.Collider:
|
||||
rotation = user.AnimController.Collider.Rotation + MathHelper.PiOver2;
|
||||
if (parentItem?.body != null)
|
||||
{
|
||||
rotation = parentItem.body.Rotation;
|
||||
}
|
||||
else if (user != null)
|
||||
{
|
||||
rotation = user.AnimController.Collider.Rotation + MathHelper.PiOver2;
|
||||
}
|
||||
break;
|
||||
case ItemSpawnInfo.SpawnRotationType.MainLimb:
|
||||
rotation = user.AnimController.MainLimb.body.TransformedRotation;
|
||||
if (user != null)
|
||||
{
|
||||
rotation = user.AnimController.MainLimb.body.TransformedRotation;
|
||||
}
|
||||
break;
|
||||
case ItemSpawnInfo.SpawnRotationType.Random:
|
||||
DebugConsole.ShowError("Random rotation is not supported for Projectiles.");
|
||||
if (projectile != null)
|
||||
{
|
||||
DebugConsole.LogError("Random rotation is not supported for Projectiles.");
|
||||
}
|
||||
else
|
||||
{
|
||||
rotation = Rand.Range(0f, MathHelper.TwoPi, Rand.RandSync.Unsynced);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException("Projectile spawn rotation type not implemented: " + chosenItemSpawnInfo.RotationType);
|
||||
throw new NotImplementedException("Item spawn rotation type not implemented: " + chosenItemSpawnInfo.RotationType);
|
||||
}
|
||||
rotation += MathHelper.ToRadians(chosenItemSpawnInfo.Rotation * user.AnimController.Dir);
|
||||
projectile.Shoot(user, ConvertUnits.ToSimUnits(worldPos), ConvertUnits.ToSimUnits(worldPos), rotation + spread, ignoredBodies: user.AnimController.Limbs.Where(l => !l.IsSevered).Select(l => l.body.FarseerBody).ToList(), createNetworkEvent: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
var body = newItem.body;
|
||||
if (body != null)
|
||||
if (user != null)
|
||||
{
|
||||
float rotation = MathHelper.ToRadians(chosenItemSpawnInfo.Rotation);
|
||||
switch (chosenItemSpawnInfo.RotationType)
|
||||
{
|
||||
case ItemSpawnInfo.SpawnRotationType.Fixed:
|
||||
if (sourceBody != null)
|
||||
{
|
||||
rotation = sourceBody.TransformRotation(chosenItemSpawnInfo.Rotation);
|
||||
}
|
||||
break;
|
||||
case ItemSpawnInfo.SpawnRotationType.Limb:
|
||||
if (sourceBody != null)
|
||||
{
|
||||
rotation += sourceBody.Rotation;
|
||||
}
|
||||
break;
|
||||
case ItemSpawnInfo.SpawnRotationType.Collider:
|
||||
if (entity is Character character)
|
||||
{
|
||||
rotation += character.AnimController.Collider.Rotation + MathHelper.PiOver2;
|
||||
}
|
||||
break;
|
||||
case ItemSpawnInfo.SpawnRotationType.MainLimb:
|
||||
if (entity is Character c)
|
||||
{
|
||||
rotation = c.AnimController.MainLimb.body.TransformedRotation;
|
||||
}
|
||||
break;
|
||||
case ItemSpawnInfo.SpawnRotationType.Random:
|
||||
rotation = Rand.Range(0f, MathHelper.TwoPi, Rand.RandSync.Unsynced);
|
||||
break;
|
||||
case ItemSpawnInfo.SpawnRotationType.Target:
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException("Spawn rotation type not implemented: " + chosenItemSpawnInfo.RotationType);
|
||||
}
|
||||
body.SetTransform(newItem.SimPosition, rotation);
|
||||
body.ApplyLinearImpulse(Rand.Vector(1) * chosenItemSpawnInfo.Impulse);
|
||||
rotation += chosenItemSpawnInfo.RotationRad * user.AnimController.Dir;
|
||||
}
|
||||
rotation += spread;
|
||||
if (projectile != null)
|
||||
{
|
||||
projectile.Shoot(user,
|
||||
ConvertUnits.ToSimUnits(worldPos),
|
||||
ConvertUnits.ToSimUnits(worldPos),
|
||||
rotation,
|
||||
ignoredBodies: user?.AnimController.Limbs.Where(l => !l.IsSevered).Select(l => l.body.FarseerBody).ToList(), createNetworkEvent: true);
|
||||
}
|
||||
else if (newItem.body != null)
|
||||
{
|
||||
newItem.body.SetTransform(newItem.SimPosition, rotation);
|
||||
Vector2 impulseDir = new Vector2(MathF.Cos(rotation), MathF.Sin(rotation));
|
||||
newItem.body.ApplyLinearImpulse(impulseDir * chosenItemSpawnInfo.Impulse);
|
||||
}
|
||||
}
|
||||
newItem.Condition = newItem.MaxCondition * chosenItemSpawnInfo.Condition;
|
||||
@@ -1930,7 +2019,7 @@ namespace Barotrauma
|
||||
{
|
||||
continue;
|
||||
}
|
||||
element.Parent.ApplyToProperty(target, property, n, CoroutineManager.UnscaledDeltaTime);
|
||||
element.Parent.ApplyToProperty(target, property, n, CoroutineManager.DeltaTime);
|
||||
}
|
||||
|
||||
foreach (Affliction affliction in element.Parent.Afflictions)
|
||||
@@ -2033,7 +2122,7 @@ namespace Barotrauma
|
||||
|
||||
if (!MathUtils.NearlyEqual(afflictionMultiplier, 1.0f))
|
||||
{
|
||||
return affliction.CreateMultiplied(afflictionMultiplier);
|
||||
return affliction.CreateMultiplied(afflictionMultiplier, affliction.Probability);
|
||||
}
|
||||
return affliction;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user