Unstable 0.1500.8.0
This commit is contained in:
@@ -688,8 +688,10 @@ namespace Barotrauma
|
||||
new GUIFrame(
|
||||
new RectTransform(Vector2.One * 0.7f, dropdownButton.RectTransform, Anchor.CenterLeft)
|
||||
{ RelativeOffset = new Vector2(0.05f, 0.0f) }, style: null);
|
||||
Color? previewingColor = null;
|
||||
dropdown.OnSelected = (component, color) =>
|
||||
{
|
||||
previewingColor = null;
|
||||
setter((Color)color);
|
||||
buttonFrame.Color = getter();
|
||||
buttonFrame.HoverColor = getter();
|
||||
@@ -727,25 +729,24 @@ namespace Barotrauma
|
||||
dropdown.Select(dropdown.ListBox.Content.GetChildIndex(childToSelect));
|
||||
|
||||
//The following exists to track mouseover to preview colors before selecting them
|
||||
bool previewingColor = false;
|
||||
new GUICustomComponent(new RectTransform(Vector2.One, buttonFrame.RectTransform),
|
||||
onUpdate: (deltaTime, component) =>
|
||||
{
|
||||
if (GUI.MouseOn is GUIFrame { Parent: { } p } hoveredFrame && dropdown.ListBox.Content.IsParentOf(hoveredFrame))
|
||||
{
|
||||
previewingColor = true;
|
||||
previewingColor ??= getter();
|
||||
Color color = (Color)(dropdown.ListBox.Content.FindChild(c =>
|
||||
c == hoveredFrame || c.IsParentOf(hoveredFrame))?.UserData ?? dropdown.SelectedData);
|
||||
c == hoveredFrame || c.IsParentOf(hoveredFrame))?.UserData ?? dropdown.SelectedData ?? getter());
|
||||
setter(color);
|
||||
buttonFrame.Color = getter();
|
||||
buttonFrame.HoverColor = getter();
|
||||
}
|
||||
else if (previewingColor)
|
||||
else if (previewingColor.HasValue)
|
||||
{
|
||||
setter((Color)dropdown.SelectedData);
|
||||
setter(previewingColor.Value);
|
||||
buttonFrame.Color = getter();
|
||||
buttonFrame.HoverColor = getter();
|
||||
previewingColor = false;
|
||||
previewingColor = null;
|
||||
}
|
||||
}, onDraw: null)
|
||||
{
|
||||
|
||||
@@ -299,7 +299,7 @@ namespace Barotrauma
|
||||
|
||||
break;
|
||||
case ServerNetObject.ENTITY_EVENT:
|
||||
int eventType = msg.ReadRangedInteger(0, 12);
|
||||
int eventType = msg.ReadRangedInteger(0, 13);
|
||||
switch (eventType)
|
||||
{
|
||||
case 0: //NetEntityEvent.Type.InventoryState
|
||||
@@ -476,6 +476,18 @@ namespace Barotrauma
|
||||
int moneyAmount = msg.ReadInt32();
|
||||
SetMoney(moneyAmount);
|
||||
break;
|
||||
case 13: //NetEntityEvent.Type.UpdatePermanentStats:
|
||||
byte savedStatValueCount = msg.ReadByte();
|
||||
StatTypes statType = (StatTypes)msg.ReadByte();
|
||||
info?.ClearSavedStatValues(statType);
|
||||
for (int i = 0; i < savedStatValueCount; i++)
|
||||
{
|
||||
string statIdentifier = msg.ReadString();
|
||||
float statValue = msg.ReadSingle();
|
||||
bool removeOnDeath = msg.ReadBoolean();
|
||||
info?.ChangeSavedStatValue(statType, statValue, statIdentifier, removeOnDeath, setValue: true);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
msg.ReadPadBits();
|
||||
|
||||
@@ -476,6 +476,7 @@ namespace Barotrauma
|
||||
if (Screen.Selected == GameMain.SubEditorScreen)
|
||||
{
|
||||
NewMessage("WARNING: Switching directly from the submarine editor to the game view may cause bugs and crashes. Use with caution.", Color.Orange);
|
||||
Entity.Spawner ??= new EntitySpawner();
|
||||
}
|
||||
GameMain.GameScreen.Select();
|
||||
}));
|
||||
@@ -488,6 +489,8 @@ namespace Barotrauma
|
||||
Submarine.MainSub = Submarine.Load(subInfo, true);
|
||||
}
|
||||
GameMain.SubEditorScreen.Select(enableAutoSave: Screen.Selected != GameMain.GameScreen);
|
||||
Entity.Spawner?.Remove();
|
||||
Entity.Spawner = null;
|
||||
}, isCheat: true));
|
||||
|
||||
commands.Add(new Command("editparticles|particleeditor", "editparticles/particleeditor: Switch to the Particle Editor to edit particle effects.", (string[] args) =>
|
||||
|
||||
@@ -139,7 +139,7 @@ namespace Barotrauma
|
||||
GameSession.UpdateTalentNotificationIndicator(talentPointNotification);
|
||||
if (Character.Controlled is { } controlled && talentResetButton != null && talentApplyButton != null)
|
||||
{
|
||||
int talentCount = selectedTalents.Count - controlled.Info.UnlockedTalents.Count;
|
||||
int talentCount = selectedTalents.Count - controlled.Info.GetUnlockedTalentsInTree().Count();
|
||||
talentResetButton.Enabled = talentApplyButton.Enabled = talentCount > 0;
|
||||
if (talentApplyButton.Enabled && talentApplyButton.FlashTimer <= 0.0f)
|
||||
{
|
||||
@@ -1254,7 +1254,7 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
selectedTalents = controlledCharacter.Info.UnlockedTalents.ToList();
|
||||
selectedTalents = controlledCharacter.Info.GetUnlockedTalentsInTree().ToList();
|
||||
|
||||
GUILayoutGroup talentFrameLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), talentFrameMain.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter)
|
||||
{
|
||||
@@ -1541,7 +1541,7 @@ namespace Barotrauma
|
||||
|
||||
string pointsLeft = controlledCharacter.Info.GetAvailableTalentPoints().ToString();
|
||||
|
||||
int talentCount = selectedTalents.Count - controlledCharacter.Info.UnlockedTalents.Count;
|
||||
int talentCount = selectedTalents.Count - controlledCharacter.Info.GetUnlockedTalentsInTree().Count();
|
||||
|
||||
if (talentCount > 0)
|
||||
{
|
||||
@@ -1612,7 +1612,7 @@ namespace Barotrauma
|
||||
private bool ResetTalentSelection(GUIButton guiButton, object userData)
|
||||
{
|
||||
Character controlledCharacter = Character.Controlled;
|
||||
selectedTalents = controlledCharacter.Info.UnlockedTalents.ToList();
|
||||
selectedTalents = controlledCharacter.Info.GetUnlockedTalentsInTree().ToList();
|
||||
UpdateTalentButtons();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -544,7 +544,7 @@ namespace Barotrauma
|
||||
if (Character.Controlled.CurrentHull == null) { return; }
|
||||
if (HumanAIController.IsBallastFloraNoticeable(Character.Controlled, Character.Controlled.CurrentHull))
|
||||
{
|
||||
if (DisplayHint("onballastflorainfected")) { return; }
|
||||
if (IsOnFriendlySub() && DisplayHint("onballastflorainfected")) { return; }
|
||||
}
|
||||
foreach (var gap in Character.Controlled.CurrentHull.ConnectedGaps)
|
||||
{
|
||||
@@ -552,7 +552,7 @@ namespace Barotrauma
|
||||
if (Vector2.DistanceSquared(Character.Controlled.WorldPosition, gap.ConnectedDoor.Item.WorldPosition) > 400 * 400) { continue; }
|
||||
if (!gap.IsRoomToRoom)
|
||||
{
|
||||
if (!(Character.Controlled.GetEquippedItem("deepdiving", InvSlotType.OuterClothes) is Item)) { continue; }
|
||||
if (!IsWearingDivingSuit()) { continue; }
|
||||
if (Character.Controlled.IsProtectedFromPressure()) { continue; }
|
||||
if (DisplayHint("divingsuitwarning", extendTextTag: false)) { return; }
|
||||
continue;
|
||||
@@ -561,10 +561,16 @@ namespace Barotrauma
|
||||
{
|
||||
if (me == Character.Controlled.CurrentHull) { continue; }
|
||||
if (!(me is Hull adjacentHull)) { continue; }
|
||||
if (!IsOnFriendlySub()) { continue; }
|
||||
if (IsWearingDivingSuit()) { continue; }
|
||||
if (adjacentHull.LethalPressure > 5.0f && DisplayHint("onadjacenthull.highpressure")) { return; }
|
||||
if (adjacentHull.WaterPercentage > 75 && !BallastHulls.Contains(adjacentHull) && DisplayHint("onadjacenthull.highwaterpercentage")) { return; }
|
||||
}
|
||||
|
||||
static bool IsWearingDivingSuit() => Character.Controlled.GetEquippedItem("deepdiving", InvSlotType.OuterClothes) is Item;
|
||||
}
|
||||
|
||||
static bool IsOnFriendlySub() => Character.Controlled.Submarine is Submarine sub && (sub.TeamID == Character.Controlled.TeamID || sub.TeamID == CharacterTeamType.FriendlyNPC);
|
||||
}
|
||||
|
||||
private static void CheckReminders()
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
|
||||
{
|
||||
if (Light.LightSprite != null && (item.body == null || item.body.Enabled) && lightBrightness > 0.0f && IsOn)
|
||||
if (Light.LightSprite != null && (item.body == null || item.body.Enabled) && lightBrightness > 0.0f && IsOn && Light.Enabled)
|
||||
{
|
||||
Vector2 origin = Light.LightSprite.Origin;
|
||||
if ((Light.LightSpriteEffect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally) { origin.X = Light.LightSprite.SourceRect.Width - origin.X; }
|
||||
|
||||
@@ -529,21 +529,30 @@ namespace Barotrauma.Items.Components
|
||||
};
|
||||
}*/
|
||||
|
||||
string name = GetRecipeNameAndAmount(selectedItem);
|
||||
string itemName = GetRecipeNameAndAmount(selectedItem);
|
||||
string name = itemName;
|
||||
|
||||
float quality = GetFabricatedItemQuality(selectedItem, user);
|
||||
if (quality > 0)
|
||||
{
|
||||
name = TextManager.GetWithVariable("itemname.quality" + (int)quality, "[itemname]", name+'\n', fallBackTag: "itemname.quality3");
|
||||
name = TextManager.GetWithVariable("itemname.quality" + (int)quality, "[itemname]", itemName + '\n', fallBackTag: "itemname.quality3");
|
||||
}
|
||||
|
||||
var nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform),
|
||||
name, textAlignment: Alignment.CenterLeft, textColor: Color.Aqua, font: GUI.SubHeadingFont, parseRichText: true)
|
||||
name, textAlignment: Alignment.TopLeft, textColor: Color.Aqua, font: GUI.SubHeadingFont, parseRichText: true)
|
||||
{
|
||||
AutoScaleHorizontal = true
|
||||
};
|
||||
|
||||
nameBlock.Padding = new Vector4(0, nameBlock.Padding.Y, nameBlock.Padding.Z, nameBlock.Padding.W);
|
||||
nameBlock.Padding = new Vector4(0, nameBlock.Padding.Y, GUI.IntScale(5), nameBlock.Padding.W);
|
||||
if (nameBlock.TextScale < 0.7f)
|
||||
{
|
||||
nameBlock.SetRichText(TextManager.GetWithVariable("itemname.quality" + (int)quality, "[itemname]", itemName, fallBackTag: "itemname.quality3"));
|
||||
nameBlock.AutoScaleHorizontal = false;
|
||||
nameBlock.TextScale = 0.7f;
|
||||
nameBlock.Wrap = true;
|
||||
nameBlock.SetTextPos();
|
||||
nameBlock.RectTransform.MinSize = new Point(0, (int)(nameBlock.TextSize.Y * nameBlock.TextScale));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(selectedItem.TargetItem.Description))
|
||||
{
|
||||
@@ -555,6 +564,7 @@ namespace Barotrauma.Items.Components
|
||||
while (description.Rect.Height + nameBlock.Rect.Height > paddedFrame.Rect.Height)
|
||||
{
|
||||
var lines = description.WrappedText.Split('\n');
|
||||
if (lines.Length <= 1) { break; }
|
||||
var newString = string.Join('\n', lines.Take(lines.Length - 1));
|
||||
description.Text = newString.Substring(0, newString.Length - 4) + "...";
|
||||
description.CalculateHeightFromText();
|
||||
|
||||
@@ -1465,6 +1465,7 @@ namespace Barotrauma.Networking
|
||||
bool respawnAllowed = inc.ReadBoolean();
|
||||
serverSettings.AllowDisguises = inc.ReadBoolean();
|
||||
serverSettings.AllowRewiring = inc.ReadBoolean();
|
||||
serverSettings.AllowFriendlyFire = inc.ReadBoolean();
|
||||
serverSettings.LockAllDefaultWires = inc.ReadBoolean();
|
||||
serverSettings.AllowRagdollButton = inc.ReadBoolean();
|
||||
GameMain.NetLobbyScreen.UsingShuttle = inc.ReadBoolean();
|
||||
|
||||
@@ -884,7 +884,7 @@ namespace Barotrauma
|
||||
// Switch the type ambience if nothing playing atm or the currently playing clip is not suitable anymore
|
||||
else if (targetMusic[typeAmbienceTrackIndex] == null || currentMusic[typeAmbienceTrackIndex] == null || !currentMusic[typeAmbienceTrackIndex].IsPlaying() || suitableTypeAmbiences.None(m => m.File == currentMusic[typeAmbienceTrackIndex].Filename))
|
||||
{
|
||||
targetMusic[mainTrackIndex] = suitableMusic.GetRandom();
|
||||
targetMusic[typeAmbienceTrackIndex] = suitableTypeAmbiences.GetRandom();
|
||||
}
|
||||
|
||||
//get the appropriate intensity layers for current situation
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>0.1500.7.0</Version>
|
||||
<Version>0.1500.8.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>0.1500.7.0</Version>
|
||||
<Version>0.1500.8.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>0.1500.7.0</Version>
|
||||
<Version>0.1500.8.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>0.1500.7.0</Version>
|
||||
<Version>0.1500.8.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>0.1500.7.0</Version>
|
||||
<Version>0.1500.8.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -32,6 +32,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnPermanentStatChanged(StatTypes statType)
|
||||
{
|
||||
if (Character == null || Character.Removed) { return; }
|
||||
GameMain.NetworkMember.CreateEntityEvent(Character, new object[] { NetEntityEvent.Type.UpdatePermanentStats, statType });
|
||||
}
|
||||
|
||||
public void ServerWrite(IWriteMessage msg)
|
||||
{
|
||||
msg.Write(ID);
|
||||
@@ -66,8 +72,8 @@ namespace Barotrauma
|
||||
msg.Write((byte)0);
|
||||
}
|
||||
// TODO: animations
|
||||
msg.Write((byte)savedStatValues.SelectMany(s => s.Value).Count());
|
||||
foreach (var savedStatValuePair in savedStatValues)
|
||||
msg.Write((byte)SavedStatValues.SelectMany(s => s.Value).Count());
|
||||
foreach (var savedStatValuePair in SavedStatValues)
|
||||
{
|
||||
foreach (var savedStatValue in savedStatValuePair.Value)
|
||||
{
|
||||
|
||||
@@ -310,7 +310,7 @@ namespace Barotrauma
|
||||
|
||||
if (extraData != null)
|
||||
{
|
||||
const int min = 0, max = 12;
|
||||
const int min = 0, max = 13;
|
||||
switch ((NetEntityEvent.Type)extraData[0])
|
||||
{
|
||||
case NetEntityEvent.Type.InventoryState:
|
||||
@@ -438,6 +438,30 @@ namespace Barotrauma
|
||||
msg.WriteRangedInteger(12, min, max);
|
||||
msg.Write(GameMain.GameSession.Campaign.Money);
|
||||
break;
|
||||
case NetEntityEvent.Type.UpdatePermanentStats:
|
||||
msg.WriteRangedInteger(13, min, max);
|
||||
if (Info == null || extraData.Length < 2 || !(extraData[1] is StatTypes statType))
|
||||
{
|
||||
msg.Write((byte)0);
|
||||
msg.Write((byte)0);
|
||||
}
|
||||
else if (!Info.SavedStatValues.ContainsKey(statType))
|
||||
{
|
||||
msg.Write((byte)0);
|
||||
msg.Write((byte)statType);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.Write((byte)Info.SavedStatValues[statType].Count);
|
||||
msg.Write((byte)statType);
|
||||
foreach (var savedStatValue in Info.SavedStatValues[statType])
|
||||
{
|
||||
msg.Write(savedStatValue.StatIdentifier);
|
||||
msg.Write(savedStatValue.StatValue);
|
||||
msg.Write(savedStatValue.RemoveOnDeath);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DebugConsole.ThrowError("Invalid NetworkEvent type for entity " + ToString() + " (" + (NetEntityEvent.Type)extraData[0] + ")");
|
||||
break;
|
||||
|
||||
@@ -1677,10 +1677,25 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
bool relativeStrength = false;
|
||||
if (args.Length > 4)
|
||||
{
|
||||
bool.TryParse(args[4], out relativeStrength);
|
||||
}
|
||||
|
||||
Character targetCharacter = (args.Length <= 2) ? client.Character : FindMatchingCharacter(args.Skip(2).ToArray());
|
||||
if (targetCharacter != null)
|
||||
{
|
||||
targetCharacter.CharacterHealth.ApplyAffliction(targetCharacter.AnimController.MainLimb, afflictionPrefab.Instantiate(afflictionStrength));
|
||||
Limb targetLimb = targetCharacter.AnimController.MainLimb;
|
||||
if (args.Length > 3)
|
||||
{
|
||||
targetLimb = targetCharacter.AnimController.Limbs.FirstOrDefault(l => l.type.ToString().Equals(args[3], StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
if (relativeStrength)
|
||||
{
|
||||
afflictionStrength *= targetCharacter.MaxVitality / afflictionPrefab.MaxStrength;
|
||||
}
|
||||
targetCharacter.CharacterHealth.ApplyAffliction(targetLimb ?? targetCharacter.AnimController.MainLimb, afflictionPrefab.Instantiate(afflictionStrength));
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -2171,6 +2186,7 @@ namespace Barotrauma
|
||||
if (client == null)
|
||||
{
|
||||
GameMain.Server.SendConsoleMessage("Client \"" + args[0] + "\" not found.", senderClient);
|
||||
return;
|
||||
}
|
||||
|
||||
var character = FindMatchingCharacter(args.Skip(1).ToArray(), false);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Barotrauma.Networking;
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -11,11 +13,65 @@ namespace Barotrauma
|
||||
get { return itemData != null; }
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(Client client)
|
||||
public CharacterCampaignData(Client client, bool giveRespawnPenaltyAffliction = false)
|
||||
{
|
||||
Name = client.Name;
|
||||
ClientEndPoint = client.Connection.EndPointString;
|
||||
SteamID = client.SteamID;
|
||||
CharacterInfo = client.CharacterInfo;
|
||||
|
||||
healthData = new XElement("health");
|
||||
client.Character?.CharacterHealth?.Save(healthData);
|
||||
if (giveRespawnPenaltyAffliction)
|
||||
{
|
||||
var respawnPenaltyAffliction = RespawnManager.GetRespawnPenaltyAffliction();
|
||||
healthData.Add(new XElement("Affliction",
|
||||
new XAttribute("identifier", respawnPenaltyAffliction.Identifier),
|
||||
new XAttribute("strength", respawnPenaltyAffliction.Strength.ToString("G", CultureInfo.InvariantCulture))));
|
||||
}
|
||||
if (client.Character?.Inventory != null)
|
||||
{
|
||||
itemData = new XElement("inventory");
|
||||
Character.SaveInventory(client.Character.Inventory, itemData);
|
||||
}
|
||||
OrderData = new XElement("orders");
|
||||
if (client.CharacterInfo != null)
|
||||
{
|
||||
CharacterInfo.SaveOrderData(client.CharacterInfo, OrderData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public CharacterCampaignData(XElement element)
|
||||
{
|
||||
Name = element.GetAttributeString("name", "Unnamed");
|
||||
ClientEndPoint = element.GetAttributeString("endpoint", null) ?? element.GetAttributeString("ip", "");
|
||||
string steamID = element.GetAttributeString("steamid", "");
|
||||
if (!string.IsNullOrEmpty(steamID))
|
||||
{
|
||||
ulong.TryParse(steamID, out ulong parsedID);
|
||||
SteamID = parsedID;
|
||||
}
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "character":
|
||||
case "characterinfo":
|
||||
CharacterInfo = new CharacterInfo(subElement);
|
||||
break;
|
||||
case "inventory":
|
||||
itemData = subElement;
|
||||
break;
|
||||
case "health":
|
||||
healthData = subElement;
|
||||
break;
|
||||
case "orders":
|
||||
OrderData = subElement;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool MatchesClient(Client client)
|
||||
|
||||
@@ -209,6 +209,11 @@ namespace Barotrauma
|
||||
//refresh the character data of clients who are still in the server
|
||||
foreach (Client c in GameMain.Server.ConnectedClients)
|
||||
{
|
||||
if (c.Character != null && c.Character.Info == null)
|
||||
{
|
||||
c.Character = null;
|
||||
}
|
||||
|
||||
if (c.HasSpawned && c.CharacterInfo != null && c.CharacterInfo.CauseOfDeath != null && c.CharacterInfo.CauseOfDeath?.Type != CauseOfDeathType.Disconnected)
|
||||
{
|
||||
//the client has opted to spawn this round with Reaper's Tax
|
||||
@@ -228,7 +233,7 @@ namespace Barotrauma
|
||||
}
|
||||
c.CharacterInfo = characterInfo;
|
||||
characterData.RemoveAll(cd => cd.MatchesClient(c));
|
||||
characterData.Add(new CharacterCampaignData(c));
|
||||
characterData.Add(new CharacterCampaignData(c));
|
||||
}
|
||||
|
||||
//refresh the character data of clients who aren't in the server anymore
|
||||
|
||||
@@ -2470,6 +2470,7 @@ namespace Barotrauma.Networking
|
||||
msg.Write(serverSettings.AllowRespawn && missionAllowRespawn);
|
||||
msg.Write(serverSettings.AllowDisguises);
|
||||
msg.Write(serverSettings.AllowRewiring);
|
||||
msg.Write(serverSettings.AllowFriendlyFire);
|
||||
msg.Write(serverSettings.LockAllDefaultWires);
|
||||
msg.Write(serverSettings.AllowRagdollButton);
|
||||
msg.Write(serverSettings.UseRespawnShuttle);
|
||||
|
||||
@@ -92,7 +92,7 @@ namespace Barotrauma
|
||||
case VoteType.Mode:
|
||||
string modeIdentifier = inc.ReadString();
|
||||
GameModePreset mode = GameModePreset.List.Find(gm => gm.Identifier == modeIdentifier);
|
||||
if (!mode.Votable) { break; }
|
||||
if (mode == null || !mode.Votable) { break; }
|
||||
sender.SetVote(voteType, mode);
|
||||
break;
|
||||
case VoteType.EndRound:
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>0.1500.7.0</Version>
|
||||
<Version>0.1500.8.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Networking;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
@@ -346,7 +347,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
#region Escape
|
||||
public abstract void Escape(float deltaTime);
|
||||
public abstract bool Escape(float deltaTime);
|
||||
|
||||
public Gap EscapeTarget { get; private set; }
|
||||
|
||||
@@ -425,17 +426,38 @@ namespace Barotrauma
|
||||
}
|
||||
if (EscapeTarget != null)
|
||||
{
|
||||
SteeringManager.SteeringSeek(EscapeTarget.SimPosition, 10);
|
||||
float sqrDist = Vector2.DistanceSquared(Character.SimPosition, EscapeTarget.SimPosition);
|
||||
if (sqrDist < 0.5f || Character.CurrentHull == null || HasValidPath(requireNonDirty: true, requireUnfinished: false) && pathSteering.CurrentPath.Finished)
|
||||
Vector2 diff = EscapeTarget.WorldPosition - Character.WorldPosition;
|
||||
float sqrDist = diff.LengthSquared();
|
||||
if (Character.CurrentHull == null || sqrDist < MathUtils.Pow2(50) || pathSteering == null || IsCurrentPathUnreachable || IsCurrentPathFinished)
|
||||
{
|
||||
// Very close to the target, outside, or at the end of the path -> just steer towards it manually without using the path
|
||||
// Very close to the target, outside, or at the end of the path -> try to steer through the gap
|
||||
SteeringManager.Reset();
|
||||
SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(EscapeTarget.WorldPosition - Character.WorldPosition));
|
||||
if (sqrDist < 4)
|
||||
pathSteering?.ResetPath();
|
||||
if (sqrDist < MathUtils.Pow2(50))
|
||||
{
|
||||
return true;
|
||||
// Very close -> just keep steering forward
|
||||
var forward = VectorExtensions.Forward(Character.AnimController.Collider.Rotation + MathHelper.PiOver2);
|
||||
SteeringManager.SteeringManual(deltaTime, forward);
|
||||
}
|
||||
else if (Character.CurrentHull == null)
|
||||
{
|
||||
// Outside -> steer away from the target
|
||||
SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(-diff));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Still inside -> steer towards the target
|
||||
SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(diff));
|
||||
}
|
||||
return sqrDist < MathUtils.Pow2(200);
|
||||
}
|
||||
else if (pathSteering != null)
|
||||
{
|
||||
pathSteering.SteeringSeek(EscapeTarget.SimPosition, weight: 1, minGapSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
SteeringManager.SteeringSeek(EscapeTarget.SimPosition, 10);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1903,7 +1903,7 @@ namespace Barotrauma
|
||||
Character.AnimController.ReleaseStuckLimbs();
|
||||
LatchOntoAI?.DeattachFromBody(reset: true, cooldown: 1);
|
||||
if (attacker == null || attacker.AiTarget == null || attacker.Removed || attacker.IsDead) { return; }
|
||||
if (Character.Params.CanInteract)
|
||||
if (Character.Params.CanInteract && attackResult.Damage > 10)
|
||||
{
|
||||
ReleaseDragTargets();
|
||||
}
|
||||
@@ -3607,12 +3607,12 @@ namespace Barotrauma
|
||||
|
||||
public bool CanPassThroughHole(Structure wall, int sectionIndex) => CanPassThroughHole(wall, sectionIndex, requiredHoleCount);
|
||||
|
||||
public override void Escape(float deltaTime)
|
||||
public override bool Escape(float deltaTime)
|
||||
{
|
||||
if (SelectedAiTarget != null && (SelectedAiTarget.Entity == null || SelectedAiTarget.Entity.Removed))
|
||||
{
|
||||
State = AIState.Idle;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
else if (SelectedTargetMemory is AITargetMemory targetMemory && SelectedAiTarget?.Entity is Character)
|
||||
{
|
||||
@@ -3653,6 +3653,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
return isSteeringThroughGap;
|
||||
|
||||
void SteerAwayFromTheEnemy()
|
||||
{
|
||||
|
||||
@@ -1364,10 +1364,7 @@ namespace Barotrauma
|
||||
ObjectiveManager.WaitTimer = waitDuration;
|
||||
}
|
||||
|
||||
public override void Escape(float deltaTime)
|
||||
{
|
||||
UpdateEscape(deltaTime, canAttackDoors: false);
|
||||
}
|
||||
public override bool Escape(float deltaTime) => UpdateEscape(deltaTime, canAttackDoors: false);
|
||||
|
||||
private void CheckCrouching(float deltaTime)
|
||||
{
|
||||
|
||||
@@ -269,7 +269,7 @@ namespace Barotrauma
|
||||
{
|
||||
var waypoint = CurrentPath.Nodes[i];
|
||||
float directDistance = Vector2.DistanceSquared(character.WorldPosition, waypoint.WorldPosition);
|
||||
if (directDistance > (pathDistance * pathDistance) || Submarine.PickBody(host.SimPosition, waypoint.SimPosition, collisionCategory: Physics.CollisionLevel) != null)
|
||||
if (directDistance > (pathDistance * pathDistance) || Submarine.PickBody(host.SimPosition, waypoint.SimPosition, collisionCategory: Physics.CollisionLevel | Physics.CollisionWall) != null)
|
||||
{
|
||||
pathDistance -= CurrentPath.GetLength(startIndex: i - 1, endIndex: i);
|
||||
continue;
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace Barotrauma
|
||||
public override string Identifier { get; set; } = "return";
|
||||
private AIObjectiveGoTo moveInsideObjective, moveInCaveObjective, moveOutsideObjective;
|
||||
private bool usingEscapeBehavior;
|
||||
private bool isSteeringThroughGap;
|
||||
public Submarine ReturnTarget { get; }
|
||||
|
||||
public AIObjectiveReturn(Character character, Character orderGiver, AIObjectiveManager objectiveManager, float priorityModifier = 1.0f) : base(character, objectiveManager, priorityModifier)
|
||||
@@ -57,7 +58,7 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
bool shouldUseEscapeBehavior = false;
|
||||
if (character.CurrentHull != null)
|
||||
if (character.CurrentHull != null || isSteeringThroughGap)
|
||||
{
|
||||
if (character.Submarine == null || !character.Submarine.IsConnectedTo(ReturnTarget))
|
||||
{
|
||||
@@ -67,8 +68,8 @@ namespace Barotrauma
|
||||
{
|
||||
HumanAIController.ResetEscape();
|
||||
}
|
||||
HumanAIController.Escape(deltaTime);
|
||||
if (HumanAIController.EscapeTarget == null || HumanAIController.IsCurrentPathUnreachable)
|
||||
isSteeringThroughGap = HumanAIController.Escape(deltaTime);
|
||||
if (!isSteeringThroughGap && (HumanAIController.EscapeTarget == null || HumanAIController.IsCurrentPathUnreachable))
|
||||
{
|
||||
Abandon = true;
|
||||
}
|
||||
@@ -92,7 +93,10 @@ namespace Barotrauma
|
||||
RemoveSubObjective(ref moveInCaveObjective);
|
||||
RemoveSubObjective(ref moveOutsideObjective);
|
||||
TryAddSubObjective(ref moveInsideObjective,
|
||||
constructor: () => new AIObjectiveGoTo(targetHull, character, objectiveManager),
|
||||
constructor: () => new AIObjectiveGoTo(targetHull, character, objectiveManager)
|
||||
{
|
||||
AllowGoingOutside = true
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref moveInsideObjective),
|
||||
onAbandon: () => Abandon = true);
|
||||
}
|
||||
@@ -110,7 +114,7 @@ namespace Barotrauma
|
||||
IsCompleted = true;
|
||||
}
|
||||
}
|
||||
else if (moveInCaveObjective == null && moveOutsideObjective == null)
|
||||
else if (!isSteeringThroughGap && moveInCaveObjective == null && moveOutsideObjective == null)
|
||||
{
|
||||
if (HumanAIController.IsInsideCave)
|
||||
{
|
||||
@@ -134,7 +138,8 @@ namespace Barotrauma
|
||||
TryAddSubObjective(ref moveInCaveObjective,
|
||||
constructor: () => new AIObjectiveGoTo(closestOutsideWaypoint, character, objectiveManager)
|
||||
{
|
||||
endNodeFilter = n => n.Waypoint == closestOutsideWaypoint
|
||||
endNodeFilter = n => n.Waypoint == closestOutsideWaypoint,
|
||||
AllowGoingOutside = true
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref moveInCaveObjective),
|
||||
onAbandon: () => Abandon = true);
|
||||
@@ -170,7 +175,10 @@ namespace Barotrauma
|
||||
RemoveSubObjective(ref moveInsideObjective);
|
||||
RemoveSubObjective(ref moveInCaveObjective);
|
||||
TryAddSubObjective(ref moveOutsideObjective,
|
||||
constructor: () => new AIObjectiveGoTo(targetHull, character, objectiveManager),
|
||||
constructor: () => new AIObjectiveGoTo(targetHull, character, objectiveManager)
|
||||
{
|
||||
AllowGoingOutside = true
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref moveOutsideObjective),
|
||||
onAbandon: () => Abandon = true);
|
||||
}
|
||||
@@ -221,6 +229,7 @@ namespace Barotrauma
|
||||
moveInCaveObjective = null;
|
||||
moveOutsideObjective = null;
|
||||
usingEscapeBehavior = false;
|
||||
isSteeringThroughGap = false;
|
||||
HumanAIController.ResetEscape();
|
||||
}
|
||||
|
||||
|
||||
@@ -236,6 +236,7 @@ namespace Barotrauma
|
||||
collisionCategory: Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionStairs);
|
||||
if (body != null)
|
||||
{
|
||||
if (body.UserData is Submarine) { return false; }
|
||||
if (body.UserData is Structure s && !s.IsPlatform) { return false; }
|
||||
if (body.UserData is Item && body.FixtureList[0].CollisionCategories.HasFlag(Physics.CollisionWall)) { return false; }
|
||||
}
|
||||
|
||||
@@ -684,18 +684,15 @@ namespace Barotrauma
|
||||
|
||||
if (rightHand != null && !rightHand.Disabled)
|
||||
{
|
||||
HandIK(rightHand, torso.SimPosition + posAddition +
|
||||
new Vector2(
|
||||
-handPos.X,
|
||||
(Math.Sign(walkPosX) == Math.Sign(Dir)) ? handPos.Y : lowerY), CurrentGroundedParams.ArmMoveStrength, CurrentGroundedParams.HandMoveStrength);
|
||||
HandIK(rightHand,
|
||||
torso.SimPosition + posAddition + new Vector2(-handPos.X, (Math.Sign(walkPosX) == Math.Sign(Dir)) ? handPos.Y : lowerY),
|
||||
CurrentGroundedParams.ArmMoveStrength, CurrentGroundedParams.HandMoveStrength);
|
||||
}
|
||||
|
||||
if (leftHand != null && !leftHand.Disabled)
|
||||
{
|
||||
HandIK(leftHand, torso.SimPosition + posAddition +
|
||||
new Vector2(
|
||||
handPos.X,
|
||||
(Math.Sign(walkPosX) == Math.Sign(-Dir)) ? handPos.Y : lowerY), CurrentGroundedParams.ArmMoveStrength, CurrentGroundedParams.HandMoveStrength);
|
||||
HandIK(leftHand,
|
||||
torso.SimPosition + posAddition + new Vector2(handPos.X, (Math.Sign(walkPosX) == Math.Sign(-Dir)) ? handPos.Y : lowerY),
|
||||
CurrentGroundedParams.ArmMoveStrength, CurrentGroundedParams.HandMoveStrength);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -705,9 +702,7 @@ namespace Barotrauma
|
||||
Vector2 footPos = colliderPos;
|
||||
if (Crouching)
|
||||
{
|
||||
footPos = new Vector2(
|
||||
Math.Sign(stepSize.X * i) * Dir * 0.4f,
|
||||
colliderPos.Y);
|
||||
footPos = new Vector2(Math.Sign(stepSize.X * i) * Dir * 0.35f, colliderPos.Y);
|
||||
if (Math.Sign(footPos.X) != Math.Sign(Dir))
|
||||
{
|
||||
//lift the foot at the back up a bit
|
||||
@@ -728,9 +723,16 @@ namespace Barotrauma
|
||||
{
|
||||
foot.DebugRefPos = colliderPos;
|
||||
foot.DebugTargetPos = footPos;
|
||||
MoveLimb(foot, footPos, CurrentGroundedParams.FootMoveStrength);
|
||||
FootIK(foot, footPos,
|
||||
CurrentGroundedParams.LegBendTorque, CurrentGroundedParams.FootTorque, CurrentGroundedParams.FootAngleInRadians);
|
||||
float footMoveForce = CurrentGroundedParams.FootMoveStrength;
|
||||
float legBendTorque = CurrentGroundedParams.LegBendTorque;
|
||||
if (Crouching)
|
||||
{
|
||||
// Keeps the pose
|
||||
legBendTorque = 100;
|
||||
footMoveForce *= 2;
|
||||
}
|
||||
MoveLimb(foot, footPos, footMoveForce);
|
||||
FootIK(foot, footPos, legBendTorque, CurrentGroundedParams.FootTorque, CurrentGroundedParams.FootAngleInRadians);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -760,6 +762,12 @@ namespace Barotrauma
|
||||
forearm.body.ApplyTorque(MathHelper.Clamp(-diff, -MathHelper.PiOver2, MathHelper.PiOver2) * forearm.Mass * 100.0f * CurrentGroundedParams.ArmMoveStrength);
|
||||
}
|
||||
}
|
||||
// Try to keep the wrist straight
|
||||
LimbJoint wrist = GetJointBetweenLimbs(foreArmType, hand.type);
|
||||
if (wrist != null)
|
||||
{
|
||||
hand.body.ApplyTorque(MathHelper.Clamp(-wrist.JointAngle, -MathHelper.PiOver2, MathHelper.PiOver2) * hand.Mass * 100f * CurrentGroundedParams.HandMoveStrength);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1008,6 +1016,12 @@ namespace Barotrauma
|
||||
speedMultiplier = Math.Min(speedMultiplier, 0.1f);
|
||||
}
|
||||
HandIK(rightHand, handPos + rightHandPos, CurrentSwimParams.ArmMoveStrength * speedMultiplier, CurrentSwimParams.HandMoveStrength * speedMultiplier);
|
||||
// Try to keep the wrist straight
|
||||
LimbJoint wrist = GetJointBetweenLimbs(LimbType.RightForearm, LimbType.RightHand);
|
||||
if (wrist != null)
|
||||
{
|
||||
rightHand.body.ApplyTorque(MathHelper.Clamp(-wrist.JointAngle, -MathHelper.PiOver2, MathHelper.PiOver2) * rightHand.Mass * 100f * CurrentSwimParams.HandMoveStrength);
|
||||
}
|
||||
}
|
||||
|
||||
if (leftHand != null && !leftHand.Disabled)
|
||||
@@ -1021,6 +1035,12 @@ namespace Barotrauma
|
||||
speedMultiplier = Math.Min(speedMultiplier, 0.1f);
|
||||
}
|
||||
HandIK(leftHand, handPos + leftHandPos, CurrentSwimParams.ArmMoveStrength * speedMultiplier, CurrentSwimParams.HandMoveStrength * speedMultiplier);
|
||||
// Try to keep the wrist straight
|
||||
LimbJoint wrist = GetJointBetweenLimbs(LimbType.LeftForearm, LimbType.LeftHand);
|
||||
if (wrist != null)
|
||||
{
|
||||
leftHand.body.ApplyTorque(MathHelper.Clamp(-wrist.JointAngle, -MathHelper.PiOver2, MathHelper.PiOver2) * leftHand.Mass * 100f * CurrentSwimParams.HandMoveStrength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -665,6 +665,7 @@ namespace Barotrauma
|
||||
public float MaxVitality => CharacterHealth.MaxVitality;
|
||||
public float MaxHealth => MaxVitality;
|
||||
public AIState AIState => AIController is EnemyAIController enemyAI ? enemyAI.State : AIState.Idle;
|
||||
public bool IsLatched => AIController is EnemyAIController enemyAI && enemyAI.LatchOntoAI != null && enemyAI.LatchOntoAI.IsAttached;
|
||||
|
||||
public float Bloodloss
|
||||
{
|
||||
@@ -2751,7 +2752,8 @@ namespace Barotrauma
|
||||
}
|
||||
else if (this != Controlled)
|
||||
{
|
||||
IsRagdolled = IsKeyDown(InputType.Ragdoll);
|
||||
wasRagdolled = IsRagdolled;
|
||||
IsRagdolled = selfRagdolled = IsKeyDown(InputType.Ragdoll);
|
||||
}
|
||||
//Keep us ragdolled if we were forced or we're too speedy to unragdoll
|
||||
else if (allowRagdoll && (!IsRagdolled || !tooFastToUnragdoll))
|
||||
@@ -2765,13 +2767,18 @@ namespace Barotrauma
|
||||
{
|
||||
wasRagdolled = IsRagdolled;
|
||||
IsRagdolled = selfRagdolled = IsKeyDown(InputType.Ragdoll); //Handle this here instead of Control because we can stop being ragdolled ourselves
|
||||
if (wasRagdolled != IsRagdolled) { ragdollingLockTimer = 0.25f; }
|
||||
if (wasRagdolled != IsRagdolled) { ragdollingLockTimer = 0.5f; }
|
||||
}
|
||||
}
|
||||
|
||||
if (!wasRagdolled && IsRagdolled && selfRagdolled)
|
||||
if (!wasRagdolled && IsRagdolled)
|
||||
{
|
||||
CheckTalents(AbilityEffectType.OnSelfRagdoll);
|
||||
if (selfRagdolled)
|
||||
{
|
||||
CheckTalents(AbilityEffectType.OnSelfRagdoll);
|
||||
}
|
||||
// currently does not work when you are stunned, like it should
|
||||
CheckTalents(AbilityEffectType.OnRagdoll);
|
||||
}
|
||||
|
||||
lowPassMultiplier = MathHelper.Lerp(lowPassMultiplier, 1.0f, 0.1f);
|
||||
@@ -3535,11 +3542,6 @@ namespace Barotrauma
|
||||
|
||||
if (Removed) { return new AttackResult(); }
|
||||
|
||||
if (attacker != null && attacker != this && GameMain.NetworkMember != null && !GameMain.NetworkMember.ServerSettings.AllowFriendlyFire)
|
||||
{
|
||||
if (attacker.TeamID == TeamID) { return new AttackResult(); }
|
||||
}
|
||||
|
||||
float closestDistance = 0.0f;
|
||||
foreach (Limb limb in AnimController.Limbs)
|
||||
{
|
||||
@@ -3602,7 +3604,11 @@ namespace Barotrauma
|
||||
|
||||
if (attacker != null && attacker != this && GameMain.NetworkMember != null && !GameMain.NetworkMember.ServerSettings.AllowFriendlyFire)
|
||||
{
|
||||
if (attacker.TeamID == TeamID) { return new AttackResult(); }
|
||||
if (attacker.TeamID == TeamID)
|
||||
{
|
||||
afflictions = afflictions.Where(a => !a.Prefab.IsBuff);
|
||||
if (!afflictions.Any()) { return new AttackResult(); }
|
||||
}
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
@@ -4385,14 +4391,14 @@ namespace Barotrauma
|
||||
info.UnlockedTalents.Add(talentPrefab.Identifier);
|
||||
if (characterTalents.Any(t => t.Prefab == talentPrefab)) { return false; }
|
||||
|
||||
#if SERVER
|
||||
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.UpdateTalents });
|
||||
#endif
|
||||
CharacterTalent characterTalent = new CharacterTalent(talentPrefab, this);
|
||||
characterTalent.ActivateTalent(addingFirstTime);
|
||||
characterTalents.Add(characterTalent);
|
||||
characterTalent.AddedThisRound = addingFirstTime;
|
||||
|
||||
#if SERVER
|
||||
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.UpdateTalents });
|
||||
#endif
|
||||
if (addingFirstTime)
|
||||
{
|
||||
OnTalentGiven(talentPrefab.Identifier);
|
||||
|
||||
@@ -216,6 +216,17 @@ namespace Barotrauma
|
||||
|
||||
public HashSet<string> UnlockedTalents { get; private set; } = new HashSet<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Endocrine boosters can unlock talents outside the user's talent tree. This method is used to cull them from the selection
|
||||
/// </summary>
|
||||
public IEnumerable<string> GetUnlockedTalentsInTree()
|
||||
{
|
||||
if (!TalentTree.JobTalentTrees.TryGetValue(Job.Prefab.Identifier, out TalentTree talentTree)) { return Enumerable.Empty<string>(); }
|
||||
|
||||
return UnlockedTalents.Where(t => talentTree.TalentIsInTree(t));
|
||||
}
|
||||
|
||||
|
||||
public int AdditionalTalentPoints { get; set; }
|
||||
|
||||
private Sprite _headSprite;
|
||||
@@ -1220,7 +1231,7 @@ namespace Barotrauma
|
||||
var experienceGainMultiplier = new AbilityValue(1f);
|
||||
if (isMissionExperience)
|
||||
{
|
||||
Character.CheckTalents(AbilityEffectType.OnGainMissionExperience, experienceGainMultiplier);
|
||||
Character?.CheckTalents(AbilityEffectType.OnGainMissionExperience, experienceGainMultiplier);
|
||||
}
|
||||
experienceGainMultiplier.Value += Character.GetStatValue(StatTypes.ExperienceGainMultiplier);
|
||||
|
||||
@@ -1252,7 +1263,7 @@ namespace Barotrauma
|
||||
public int GetAvailableTalentPoints()
|
||||
{
|
||||
// hashset always has at least 1
|
||||
return Math.Max(GetTotalTalentPoints() - UnlockedTalents.Count, 0);
|
||||
return Math.Max(GetTotalTalentPoints() - GetUnlockedTalentsInTree().Count(), 0);
|
||||
}
|
||||
|
||||
public float GetProgressTowardsNextLevel()
|
||||
@@ -1297,6 +1308,8 @@ namespace Barotrauma
|
||||
|
||||
partial void OnExperienceChanged(int prevAmount, int newAmount);
|
||||
|
||||
partial void OnPermanentStatChanged(StatTypes statType);
|
||||
|
||||
public void Rename(string newName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(newName)) { return; }
|
||||
@@ -1362,7 +1375,7 @@ namespace Barotrauma
|
||||
Job.Save(charElement);
|
||||
|
||||
XElement savedStatElement = new XElement("savedstatvalues");
|
||||
foreach (var statValuePair in savedStatValues)
|
||||
foreach (var statValuePair in SavedStatValues)
|
||||
{
|
||||
foreach (var savedStat in statValuePair.Value)
|
||||
{
|
||||
@@ -1708,26 +1721,42 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
// This could maybe be a LookUp instead?
|
||||
private readonly Dictionary<StatTypes, List<SavedStatValue>> savedStatValues = new Dictionary<StatTypes, List<SavedStatValue>>();
|
||||
public readonly Dictionary<StatTypes, List<SavedStatValue>> SavedStatValues = new Dictionary<StatTypes, List<SavedStatValue>>();
|
||||
|
||||
public void ResetSavedStatValues()
|
||||
public void ClearSavedStatValues()
|
||||
{
|
||||
foreach (var savedStatValue in savedStatValues.SelectMany(s => s.Value))
|
||||
foreach (StatTypes statType in SavedStatValues.Keys)
|
||||
{
|
||||
if (savedStatValue.RemoveOnDeath)
|
||||
{
|
||||
savedStatValue.StatValue = 0f;
|
||||
}
|
||||
OnPermanentStatChanged(statType);
|
||||
}
|
||||
SavedStatValues.Clear();
|
||||
}
|
||||
|
||||
public void ClearSavedStatValues(StatTypes statType)
|
||||
{
|
||||
SavedStatValues.Remove(statType);
|
||||
OnPermanentStatChanged(statType);
|
||||
}
|
||||
|
||||
public void ResetSavedStatValue(string statIdentifier)
|
||||
{
|
||||
savedStatValues.SelectMany(s => s.Value).Where(s => s.StatIdentifier == statIdentifier).ForEach(v => v.StatValue = 0f);
|
||||
foreach (StatTypes statType in SavedStatValues.Keys)
|
||||
{
|
||||
bool changed = false;
|
||||
foreach (SavedStatValue savedStatValue in SavedStatValues[statType])
|
||||
{
|
||||
if (savedStatValue.StatIdentifier != statIdentifier) { continue; }
|
||||
if (MathUtils.NearlyEqual(savedStatValue.StatValue, 0.0f)) { continue; }
|
||||
savedStatValue.StatValue = 0.0f;
|
||||
changed = true;
|
||||
}
|
||||
if (changed) { OnPermanentStatChanged(statType); }
|
||||
}
|
||||
}
|
||||
|
||||
public float GetSavedStatValue(StatTypes statType)
|
||||
{
|
||||
if (savedStatValues.TryGetValue(statType, out var statValues))
|
||||
if (SavedStatValues.TryGetValue(statType, out var statValues))
|
||||
{
|
||||
return statValues.Sum(v => v.StatValue);
|
||||
}
|
||||
@@ -1738,7 +1767,7 @@ namespace Barotrauma
|
||||
}
|
||||
public float GetSavedStatValue(StatTypes statType, string statIdentifier)
|
||||
{
|
||||
if (savedStatValues.TryGetValue(statType, out var statValues))
|
||||
if (SavedStatValues.TryGetValue(statType, out var statValues))
|
||||
{
|
||||
return statValues.Where(s => s.StatIdentifier.Equals(statIdentifier, StringComparison.OrdinalIgnoreCase)).Sum(v => v.StatValue);
|
||||
}
|
||||
@@ -1750,19 +1779,24 @@ namespace Barotrauma
|
||||
|
||||
public void ChangeSavedStatValue(StatTypes statType, float value, string statIdentifier, bool removeOnDeath, bool removeAfterRound = false, float maxValue = float.MaxValue, bool setValue = false)
|
||||
{
|
||||
if (!savedStatValues.ContainsKey(statType))
|
||||
if (!SavedStatValues.ContainsKey(statType))
|
||||
{
|
||||
savedStatValues.Add(statType, new List<SavedStatValue>());
|
||||
SavedStatValues.Add(statType, new List<SavedStatValue>());
|
||||
}
|
||||
|
||||
if (savedStatValues[statType].FirstOrDefault(s => s.StatIdentifier == statIdentifier) is SavedStatValue savedStat)
|
||||
bool changed = false;
|
||||
if (SavedStatValues[statType].FirstOrDefault(s => s.StatIdentifier == statIdentifier) is SavedStatValue savedStat)
|
||||
{
|
||||
float prevValue = savedStat.StatValue;
|
||||
savedStat.StatValue = setValue ? value : MathHelper.Min(savedStat.StatValue + value, maxValue);
|
||||
changed = !MathUtils.NearlyEqual(savedStat.StatValue, prevValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
savedStatValues[statType].Add(new SavedStatValue(statIdentifier, MathHelper.Min(value, maxValue), removeOnDeath, removeAfterRound));
|
||||
SavedStatValues[statType].Add(new SavedStatValue(statIdentifier, MathHelper.Min(value, maxValue), removeOnDeath, removeAfterRound));
|
||||
changed = true;
|
||||
}
|
||||
if (changed) { OnPermanentStatChanged(statType); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace Barotrauma.Abilities
|
||||
protected readonly List<StatusEffect> statusEffects;
|
||||
|
||||
private readonly bool nearbyCharactersAppliesToSelf;
|
||||
private readonly bool nearbyCharactersAppliesToAllies;
|
||||
private readonly bool applyToSelected;
|
||||
|
||||
readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
|
||||
@@ -20,6 +21,7 @@ namespace Barotrauma.Abilities
|
||||
statusEffects = CharacterAbilityGroup.ParseStatusEffects(CharacterTalent, abilityElement.GetChildElement("statuseffects"));
|
||||
applyToSelected = abilityElement.GetAttributeBool("applytoselected", false);
|
||||
nearbyCharactersAppliesToSelf = abilityElement.GetAttributeBool("nearbycharactersappliestoself", true);
|
||||
nearbyCharactersAppliesToAllies = abilityElement.GetAttributeBool("nearbycharactersappliestoallies", true);
|
||||
}
|
||||
|
||||
protected void ApplyEffectSpecific(Character targetCharacter)
|
||||
@@ -40,6 +42,10 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
targets.RemoveAll(c => c == Character);
|
||||
}
|
||||
if (!nearbyCharactersAppliesToAllies)
|
||||
{
|
||||
targets.RemoveAll(c => c is Character otherCharacter && HumanAIController.IsFriendly(otherCharacter, Character));
|
||||
}
|
||||
statusEffect.SetUser(Character);
|
||||
statusEffect.Apply(ActionType.OnAbility, EffectDeltaTime, targetCharacter, targets);
|
||||
}
|
||||
@@ -56,17 +62,20 @@ namespace Barotrauma.Abilities
|
||||
}
|
||||
}
|
||||
protected override void ApplyEffect()
|
||||
{
|
||||
ApplyEffectSpecific(Character);
|
||||
}
|
||||
|
||||
protected override void ApplyEffect(AbilityObject abilityObject)
|
||||
{
|
||||
if (applyToSelected && Character.SelectedCharacter is Character selectedCharacter)
|
||||
{
|
||||
ApplyEffectSpecific(selectedCharacter);
|
||||
}
|
||||
else if ((abilityObject as IAbilityCharacter)?.Character is Character targetCharacter)
|
||||
else
|
||||
{
|
||||
ApplyEffectSpecific(Character);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ApplyEffect(AbilityObject abilityObject)
|
||||
{
|
||||
if ((abilityObject as IAbilityCharacter)?.Character is Character targetCharacter)
|
||||
{
|
||||
ApplyEffectSpecific(targetCharacter);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Barotrauma.Abilities
|
||||
private readonly bool setValue;
|
||||
|
||||
//private readonly float maximumValue;
|
||||
|
||||
public override bool AllowClientSimulation => true;
|
||||
public override bool AppliesEffectOnIntervalUpdate => true;
|
||||
|
||||
public CharacterAbilityGivePermanentStat(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
private readonly string statIdentifier;
|
||||
public override bool AppliesEffectOnIntervalUpdate => true;
|
||||
public override bool AllowClientSimulation => true;
|
||||
|
||||
public CharacterAbilityResetPermanentStat(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace Barotrauma.Abilities
|
||||
}
|
||||
}
|
||||
|
||||
if (closestCharacter.SelectedConstruction == null || !Character.SelectedConstruction.HasTag(tag)) { return; }
|
||||
if (closestCharacter.SelectedConstruction == null || !closestCharacter.SelectedConstruction.HasTag(tag)) { return; }
|
||||
|
||||
if (closestDistance < squaredMaxDistance)
|
||||
{
|
||||
|
||||
@@ -66,6 +66,11 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public bool TalentIsInTree(string talentIdentifier)
|
||||
{
|
||||
return TalentSubTrees.SelectMany(s => s.TalentOptionStages.SelectMany(o => o.Talents.Select(t => t.Identifier))).Any(c => c == talentIdentifier);
|
||||
}
|
||||
|
||||
public static void LoadFromFile(ContentFile file)
|
||||
{
|
||||
DebugConsole.Log("Loading talent tree: " + file.Path);
|
||||
|
||||
@@ -602,7 +602,7 @@ namespace Barotrauma
|
||||
}
|
||||
}));
|
||||
|
||||
commands.Add(new Command("giveaffliction", "giveaffliction [affliction name] [affliction strength] [character name] [limb type]: Add an affliction to a character. If the name parameter is omitted, the affliction is added to the controlled character.", (string[] args) =>
|
||||
commands.Add(new Command("giveaffliction", "giveaffliction [affliction name] [affliction strength] [character name] [limb type] [use relative strength]: Add an affliction to a character. If the name parameter is omitted, the affliction is added to the controlled character.", (string[] args) =>
|
||||
{
|
||||
if (args.Length < 2) { return; }
|
||||
|
||||
@@ -622,14 +622,12 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
bool relativeStrength = false;
|
||||
if (args.Length > 2)
|
||||
if (args.Length > 4)
|
||||
{
|
||||
bool.TryParse(args[2], out relativeStrength);
|
||||
bool.TryParse(args[4], out relativeStrength);
|
||||
}
|
||||
|
||||
Character targetCharacter = (relativeStrength || args.Length <= 2) ? Character.Controlled : FindMatchingCharacter(new string[] { args[2] });
|
||||
|
||||
|
||||
Character targetCharacter = args.Length <= 2 ? Character.Controlled : FindMatchingCharacter(new string[] { args[2] });
|
||||
if (targetCharacter != null)
|
||||
{
|
||||
Limb targetLimb = targetCharacter.AnimController.MainLimb;
|
||||
@@ -1889,9 +1887,9 @@ namespace Barotrauma
|
||||
if (!IsCommandPermitted(splitCommand[0].ToLowerInvariant(), GameMain.Client))
|
||||
{
|
||||
#if DEBUG
|
||||
AddWarning("You're not permitted to use the command \"{matchingCommand.Name}\". Executing the command anyway because this is a debug build.");
|
||||
AddWarning($"You're not permitted to use the command \"{splitCommand[0].ToLowerInvariant()}\". Executing the command anyway because this is a debug build.");
|
||||
#else
|
||||
ThrowError("You're not permitted to use the command \"" + splitCommand[0].ToLowerInvariant() + "\"!");
|
||||
ThrowError($"You're not permitted to use the command \"{splitCommand[0].ToLowerInvariant()}\"!");
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
OnReduceAffliction,
|
||||
OnAddDamageAffliction,
|
||||
OnSelfRagdoll,
|
||||
OnRagdoll,
|
||||
OnRoundEnd,
|
||||
OnAnyMissionCompleted,
|
||||
OnAllMissionsCompleted,
|
||||
|
||||
@@ -179,7 +179,7 @@ namespace Barotrauma
|
||||
if (eventSet == null) { return; }
|
||||
if (eventSet.OncePerOutpost)
|
||||
{
|
||||
foreach (EventPrefab ep in eventSet.EventPrefabs.Select(e => e.prefab))
|
||||
foreach (EventPrefab ep in eventSet.EventPrefabs.SelectMany(e => e.Prefabs))
|
||||
{
|
||||
if (!level.LevelData.NonRepeatableEvents.Contains(ep))
|
||||
{
|
||||
@@ -434,23 +434,31 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
var suitablePrefabs = eventSet.EventPrefabs.FindAll(e =>
|
||||
string.IsNullOrEmpty(e.prefab.BiomeIdentifier) ||
|
||||
e.prefab.BiomeIdentifier.Equals(level.LevelData?.Biome?.Identifier, StringComparison.OrdinalIgnoreCase));
|
||||
bool isPrefabSuitable(EventPrefab p)
|
||||
=> string.IsNullOrEmpty(p.BiomeIdentifier) ||
|
||||
p.BiomeIdentifier.Equals(level.LevelData?.Biome?.Identifier, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
var suitablePrefabSubsets = eventSet.EventPrefabs
|
||||
.FindAll(p => p.Prefabs.Any(isPrefabSuitable));
|
||||
|
||||
for (int i = 0; i < applyCount; i++)
|
||||
{
|
||||
if (eventSet.ChooseRandom)
|
||||
{
|
||||
if (suitablePrefabs.Count > 0)
|
||||
if (suitablePrefabSubsets.Count > 0)
|
||||
{
|
||||
var unusedEvents = new List<(EventPrefab prefab, float commonness, float probability)>(suitablePrefabs);
|
||||
var unusedEvents = suitablePrefabSubsets.ToList();
|
||||
for (int j = 0; j < eventSet.EventCount; j++)
|
||||
{
|
||||
if (unusedEvents.All(e => CalculateCommonness(e.prefab, e.commonness) <= 0.0f)) { break; }
|
||||
(EventPrefab eventPrefab, float commonness, float probability) = ToolBox.SelectWeightedRandom(unusedEvents, unusedEvents.Select(e => CalculateCommonness(e.prefab, e.commonness)).ToList(), rand);
|
||||
if (eventPrefab != null && rand.NextDouble() <= probability)
|
||||
if (unusedEvents.All(e => e.Prefabs.All(p => CalculateCommonness(p, e.Commonness) <= 0.0f))) { break; }
|
||||
EventSet.SubEventPrefab subEventPrefab = ToolBox.SelectWeightedRandom(unusedEvents, unusedEvents.Select(e => e.Prefabs.Max(p => CalculateCommonness(p, e.Commonness))).ToList(), rand);
|
||||
(IEnumerable<EventPrefab> eventPrefabs, float commonness, float probability) = subEventPrefab;
|
||||
if (eventPrefabs != null && rand.NextDouble() <= probability)
|
||||
{
|
||||
var finalPrefabs = eventPrefabs.Where(isPrefabSuitable).ToArray();
|
||||
var finalPrefabCommonnesses = finalPrefabs.Select(p => p.Commonness).ToArray();
|
||||
var eventPrefab = ToolBox.SelectWeightedRandom(finalPrefabs, finalPrefabCommonnesses, rand);
|
||||
|
||||
var newEvent = eventPrefab.CreateInstance();
|
||||
if (newEvent == null) { continue; }
|
||||
newEvent.Init(true);
|
||||
@@ -465,7 +473,7 @@ namespace Barotrauma
|
||||
selectedEvents.Add(eventSet, new List<Event>());
|
||||
}
|
||||
selectedEvents[eventSet].Add(newEvent);
|
||||
unusedEvents.Remove((eventPrefab, commonness, probability));
|
||||
unusedEvents.Remove(subEventPrefab);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -480,9 +488,13 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ((EventPrefab eventPrefab, float commonness, float probability) in suitablePrefabs)
|
||||
foreach ((IEnumerable<EventPrefab> eventPrefabs, float commonness, float probability) in suitablePrefabSubsets)
|
||||
{
|
||||
if (rand.NextDouble() > probability) { continue; }
|
||||
|
||||
var finalPrefabs = eventPrefabs.Where(isPrefabSuitable).ToArray();
|
||||
var finalPrefabCommonnesses = finalPrefabs.Select(p => p.Commonness).ToArray();
|
||||
var eventPrefab = ToolBox.SelectWeightedRandom(finalPrefabs, finalPrefabCommonnesses, rand);
|
||||
var newEvent = eventPrefab.CreateInstance();
|
||||
if (newEvent == null) { continue; }
|
||||
newEvent.Init(true);
|
||||
@@ -866,7 +878,7 @@ namespace Barotrauma
|
||||
|
||||
private float CalculateDistanceTraveled()
|
||||
{
|
||||
if (level == null) { return 0.0f; }
|
||||
if (level == null || pathFinder == null) { return 0.0f; }
|
||||
var refEntity = GetRefEntity();
|
||||
if (refEntity == null) { return 0.0f; }
|
||||
Vector2 target = ConvertUnits.ToSimUnits(level.EndPosition);
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Barotrauma
|
||||
BiomeIdentifier = ConfigElement.GetAttributeString("biome", string.Empty);
|
||||
Commonness = element.GetAttributeFloat("commonness", 1.0f);
|
||||
Probability = Math.Clamp(element.GetAttributeFloat(1.0f, "probability", "spawnprobability"), 0, 1);
|
||||
TriggerEventCooldown = element.GetAttributeBool("triggereventcooldown", true);
|
||||
TriggerEventCooldown = element.GetAttributeBool("triggereventcooldown", EventType != typeof(ScriptedEvent));
|
||||
|
||||
UnlockPathEvent = element.GetAttributeBool("unlockpathevent", false);
|
||||
UnlockPathTooltip = element.GetAttributeString("unlockpathtooltip", "lockedpathtooltip");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
@@ -48,10 +49,10 @@ namespace Barotrauma
|
||||
List<EventPrefab> eventPrefabs = new List<EventPrefab>(PrefabList);
|
||||
foreach (var eventSet in List)
|
||||
{
|
||||
eventPrefabs.AddRange(eventSet.EventPrefabs.Select(ep => ep.prefab));
|
||||
eventPrefabs.AddRange(eventSet.EventPrefabs.SelectMany(ep => ep.Prefabs));
|
||||
foreach (var childSet in eventSet.ChildSets)
|
||||
{
|
||||
eventPrefabs.AddRange(childSet.EventPrefabs.Select(ep => ep.prefab));
|
||||
eventPrefabs.AddRange(childSet.EventPrefabs.SelectMany(ep => ep.Prefabs));
|
||||
}
|
||||
}
|
||||
return eventPrefabs;
|
||||
@@ -98,7 +99,48 @@ namespace Barotrauma
|
||||
|
||||
public readonly Dictionary<string, float> Commonness;
|
||||
|
||||
public readonly List<(EventPrefab prefab, float commonness, float probability)> EventPrefabs;
|
||||
public struct SubEventPrefab
|
||||
{
|
||||
public SubEventPrefab(string debugIdentifier, string[] prefabIdentifiers, float? commonness, float? probability)
|
||||
{
|
||||
EventPrefab tryFindPrefab(string id)
|
||||
{
|
||||
var prefab = PrefabList.Find(p => p.Identifier.Equals(id, StringComparison.OrdinalIgnoreCase));
|
||||
if (prefab is null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in event set \"{debugIdentifier}\" - could not find the event prefab \"{id}\".");
|
||||
}
|
||||
return prefab;
|
||||
}
|
||||
|
||||
this.Prefabs = prefabIdentifiers
|
||||
.Select(tryFindPrefab)
|
||||
.Where(p => p != null)
|
||||
.ToImmutableArray();
|
||||
this.Commonness = commonness ?? this.Prefabs.Select(p => p.Commonness).Max();
|
||||
this.Probability = probability ?? this.Prefabs.Select(p => p.Probability).Max();
|
||||
}
|
||||
|
||||
public SubEventPrefab(EventPrefab prefab, float commonness, float probability)
|
||||
{
|
||||
Prefabs = prefab.ToEnumerable().ToImmutableArray();
|
||||
Commonness = commonness;
|
||||
Probability = probability;
|
||||
}
|
||||
|
||||
public readonly ImmutableArray<EventPrefab> Prefabs;
|
||||
public readonly float Commonness;
|
||||
public readonly float Probability;
|
||||
|
||||
public void Deconstruct(out IEnumerable<EventPrefab> prefabs, out float commonness, out float probability)
|
||||
{
|
||||
prefabs = Prefabs;
|
||||
commonness = Commonness;
|
||||
probability = Probability;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly List<SubEventPrefab> EventPrefabs;
|
||||
|
||||
public readonly List<EventSet> ChildSets;
|
||||
|
||||
@@ -112,7 +154,7 @@ namespace Barotrauma
|
||||
{
|
||||
DebugIdentifier = element.GetAttributeString("identifier", null) ?? debugIdentifier;
|
||||
Commonness = new Dictionary<string, float>();
|
||||
EventPrefabs = new List<(EventPrefab prefab, float commonness, float probability)>();
|
||||
EventPrefabs = new List<SubEventPrefab>();
|
||||
ChildSets = new List<EventSet>();
|
||||
|
||||
BiomeIdentifier = element.GetAttributeString("biome", string.Empty);
|
||||
@@ -178,23 +220,20 @@ namespace Barotrauma
|
||||
//an element with just an identifier = reference to an event prefab
|
||||
if (!subElement.HasElements && subElement.Attributes().First().Name.ToString().Equals("identifier", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string identifier = subElement.GetAttributeString("identifier", "");
|
||||
var prefab = PrefabList.Find(p => p.Identifier.Equals(identifier, StringComparison.OrdinalIgnoreCase));
|
||||
if (prefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in event set \"{debugIdentifier}\" - could not find the event prefab \"{identifier}\".");
|
||||
}
|
||||
else
|
||||
{
|
||||
float commonness = subElement.GetAttributeFloat("commonness", prefab.Commonness);
|
||||
float probability = subElement.GetAttributeFloat("probability", prefab.Probability);
|
||||
EventPrefabs.Add((prefab, commonness, probability));
|
||||
}
|
||||
string[] identifiers = subElement.GetAttributeStringArray("identifier", Array.Empty<string>());
|
||||
|
||||
float commonness = subElement.GetAttributeFloat("commonness", -1f);
|
||||
float probability = subElement.GetAttributeFloat("probability", -1f);
|
||||
EventPrefabs.Add(new SubEventPrefab(
|
||||
debugIdentifier,
|
||||
identifiers,
|
||||
commonness>=0f ? commonness : (float?)null,
|
||||
probability>=0f ? probability : (float?)null));
|
||||
}
|
||||
else
|
||||
{
|
||||
var prefab = new EventPrefab(subElement);
|
||||
EventPrefabs.Add((prefab, prefab.Commonness, prefab.Probability));
|
||||
EventPrefabs.Add(new SubEventPrefab(prefab, prefab.Commonness, prefab.Probability));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -346,13 +385,13 @@ namespace Barotrauma
|
||||
{
|
||||
if (thisSet.ChooseRandom)
|
||||
{
|
||||
var unusedEvents = new List<(EventPrefab prefab, float commonness, float probability)>(thisSet.EventPrefabs);
|
||||
var unusedEvents = thisSet.EventPrefabs.ToList();
|
||||
for (int i = 0; i < thisSet.EventCount; i++)
|
||||
{
|
||||
var eventPrefab = ToolBox.SelectWeightedRandom(unusedEvents, unusedEvents.Select(e => e.commonness).ToList(), Rand.RandSync.Unsynced);
|
||||
if (eventPrefab.prefab != null)
|
||||
var eventPrefab = ToolBox.SelectWeightedRandom(unusedEvents, unusedEvents.Select(e => e.Commonness).ToList(), Rand.RandSync.Unsynced);
|
||||
if (eventPrefab.Prefabs.Any(p => p != null))
|
||||
{
|
||||
AddEvent(stats, eventPrefab.prefab);
|
||||
AddEvents(stats, eventPrefab.Prefabs);
|
||||
unusedEvents.Remove(eventPrefab);
|
||||
}
|
||||
}
|
||||
@@ -361,7 +400,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (var eventPrefab in thisSet.EventPrefabs)
|
||||
{
|
||||
AddEvent(stats, eventPrefab.prefab);
|
||||
AddEvents(stats, eventPrefab.Prefabs);
|
||||
}
|
||||
}
|
||||
foreach (var childSet in thisSet.ChildSets)
|
||||
@@ -370,6 +409,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
static void AddEvents(EventDebugStats stats, IEnumerable<EventPrefab> eventPrefabs)
|
||||
=> eventPrefabs.ForEach(p => AddEvent(stats, p));
|
||||
|
||||
static void AddEvent(EventDebugStats stats, EventPrefab eventPrefab)
|
||||
{
|
||||
if (eventPrefab.EventType == typeof(MonsterEvent))
|
||||
|
||||
@@ -363,7 +363,7 @@ namespace Barotrauma
|
||||
#if CLIENT
|
||||
foreach (Character character in crewCharacters)
|
||||
{
|
||||
character.Info.GiveExperience(experienceGain, isMissionExperience: true);
|
||||
character.Info?.GiveExperience(experienceGain, isMissionExperience: true);
|
||||
}
|
||||
#else
|
||||
foreach (Barotrauma.Networking.Client c in GameMain.Server.ConnectedClients)
|
||||
|
||||
@@ -218,12 +218,12 @@ namespace Barotrauma
|
||||
return validContainers;
|
||||
}
|
||||
|
||||
private static readonly float[] qualityCommonnesses = new float[Quality.MaxQuality + 1]
|
||||
private static readonly (int quality, float commonness)[] qualityCommonnesses = new (int quality, float commonness)[Quality.MaxQuality + 1]
|
||||
{
|
||||
0.85f,
|
||||
0.125f,
|
||||
0.0225f,
|
||||
0.0025f,
|
||||
(0, 0.85f),
|
||||
(1, 0.125f),
|
||||
(2, 0.0225f),
|
||||
(3, 0.0025f),
|
||||
};
|
||||
|
||||
private static List<Item> SpawnItem(ItemPrefab itemPrefab, List<ItemContainer> containers, KeyValuePair<ItemContainer, PreferredContainer> validContainer, float difficultyModifier)
|
||||
@@ -243,20 +243,15 @@ namespace Barotrauma
|
||||
containers.Remove(validContainer.Key);
|
||||
break;
|
||||
}
|
||||
if (!validContainer.Key.Inventory.CanBePut(itemPrefab)) { break; }
|
||||
|
||||
int quality = 0;
|
||||
float qualityCommmonnessSum = qualityCommonnesses.Sum();
|
||||
float randomNumber = Rand.Range(0f, qualityCommmonnessSum, Rand.RandSync.Server);
|
||||
for (int k = qualityCommonnesses.Length - 1; k >= 0; k--)
|
||||
{
|
||||
if (randomNumber < qualityCommonnesses[k])
|
||||
{
|
||||
quality = k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var existingItem = validContainer.Key.Inventory.AllItems.FirstOrDefault(it => it.prefab == itemPrefab);
|
||||
int quality =
|
||||
existingItem?.Quality ??
|
||||
ToolBox.SelectWeightedRandom(
|
||||
qualityCommonnesses.Select(q => q.quality).ToList(),
|
||||
qualityCommonnesses.Select(q => q.commonness).ToList(),
|
||||
Rand.RandSync.Server);
|
||||
if (!validContainer.Key.Inventory.CanBePut(itemPrefab, quality: quality)) { break; }
|
||||
var item = new Item(itemPrefab, validContainer.Key.Item.Position, validContainer.Key.Item.Submarine)
|
||||
{
|
||||
SpawnedInOutpost = validContainer.Key.Item.SpawnedInOutpost,
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using Barotrauma.Networking;
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -29,65 +27,6 @@ namespace Barotrauma
|
||||
private XElement healthData;
|
||||
public XElement OrderData { get; private set; }
|
||||
|
||||
partial void InitProjSpecific(Client client);
|
||||
public CharacterCampaignData(Client client, bool giveRespawnPenaltyAffliction = false)
|
||||
{
|
||||
Name = client.Name;
|
||||
InitProjSpecific(client);
|
||||
|
||||
healthData = new XElement("health");
|
||||
client.Character?.CharacterHealth?.Save(healthData);
|
||||
if (giveRespawnPenaltyAffliction)
|
||||
{
|
||||
var respawnPenaltyAffliction = RespawnManager.GetRespawnPenaltyAffliction();
|
||||
healthData.Add(new XElement("Affliction",
|
||||
new XAttribute("identifier", respawnPenaltyAffliction.Identifier),
|
||||
new XAttribute("strength", respawnPenaltyAffliction.Strength.ToString("G", CultureInfo.InvariantCulture))));
|
||||
}
|
||||
if (client.Character?.Inventory != null)
|
||||
{
|
||||
itemData = new XElement("inventory");
|
||||
Character.SaveInventory(client.Character.Inventory, itemData);
|
||||
}
|
||||
OrderData = new XElement("orders");
|
||||
if (client.Character != null)
|
||||
{
|
||||
CharacterInfo.SaveOrderData(client.Character.Info, OrderData);
|
||||
}
|
||||
}
|
||||
|
||||
public CharacterCampaignData(XElement element)
|
||||
{
|
||||
Name = element.GetAttributeString("name", "Unnamed");
|
||||
ClientEndPoint = element.GetAttributeString("endpoint", null) ?? element.GetAttributeString("ip", "");
|
||||
string steamID = element.GetAttributeString("steamid", "");
|
||||
if (!string.IsNullOrEmpty(steamID))
|
||||
{
|
||||
ulong.TryParse(steamID, out ulong parsedID);
|
||||
SteamID = parsedID;
|
||||
}
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "character":
|
||||
case "characterinfo":
|
||||
CharacterInfo = new CharacterInfo(subElement);
|
||||
break;
|
||||
case "inventory":
|
||||
itemData = subElement;
|
||||
break;
|
||||
case "health":
|
||||
healthData = subElement;
|
||||
break;
|
||||
case "orders":
|
||||
OrderData = subElement;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Refresh(Character character)
|
||||
{
|
||||
healthData = new XElement("health");
|
||||
|
||||
@@ -141,10 +141,10 @@ namespace Barotrauma
|
||||
(SlotTypes[i] == InvSlotType.Any || slots[i].ItemCount < 1);
|
||||
}
|
||||
|
||||
public override bool CanBePutInSlot(ItemPrefab itemPrefab, int i, float? condition)
|
||||
public override bool CanBePutInSlot(ItemPrefab itemPrefab, int i, float? condition, int? quality = null)
|
||||
{
|
||||
return
|
||||
base.CanBePutInSlot(itemPrefab, i, condition) &&
|
||||
base.CanBePutInSlot(itemPrefab, i, condition, quality) &&
|
||||
(SlotTypes[i] == InvSlotType.Any || slots[i].ItemCount < 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -268,7 +268,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
string[] allSpecies = SpeciesName.Split(',');
|
||||
string species = allSpecies.GetRandom().Trim();
|
||||
Entity.Spawner.AddToSpawnQueue(species, pos);
|
||||
Entity.Spawner?.AddToSpawnQueue(species, pos);
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(ItemIdentifier))
|
||||
{
|
||||
@@ -282,7 +282,7 @@ namespace Barotrauma.Items.Components
|
||||
pos -= sub.Position;
|
||||
}
|
||||
|
||||
Entity.Spawner.AddToSpawnQueue(prefab, pos, item.Submarine);
|
||||
Entity.Spawner?.AddToSpawnQueue(prefab, pos, item.Submarine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -642,10 +642,8 @@ namespace Barotrauma.Items.Components
|
||||
item.Drop(character);
|
||||
item.SetTransform(ConvertUnits.ToSimUnits(GetAttachPosition(character)), 0.0f, findNewHull: false);
|
||||
}
|
||||
AttachToWall();
|
||||
}
|
||||
|
||||
AttachToWall();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -291,13 +291,16 @@ namespace Barotrauma.Items.Components
|
||||
int index = Inventory.FindIndex(containedItem);
|
||||
if (index >= 0 && index < slotRestrictions.Length)
|
||||
{
|
||||
RelatedItem ri = slotRestrictions[index].ContainableItems?.Find(ci => ci.MatchesItem(containedItem));
|
||||
if (ri != null)
|
||||
if (slotRestrictions[index].ContainableItems != null)
|
||||
{
|
||||
activeContainedItems.RemoveAll(i => i.Item == containedItem);
|
||||
foreach (StatusEffect effect in ri.statusEffects)
|
||||
foreach (var containableItem in slotRestrictions[index].ContainableItems)
|
||||
{
|
||||
activeContainedItems.Add(new ActiveContainedItem(containedItem, effect, ri.ExcludeBroken));
|
||||
if (!containableItem.MatchesItem(containedItem)) { continue; }
|
||||
foreach (StatusEffect effect in containableItem.statusEffects)
|
||||
{
|
||||
activeContainedItems.Add(new ActiveContainedItem(containedItem, effect, containableItem.ExcludeBroken));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ namespace Barotrauma.Items.Components
|
||||
public OxygenGenerator(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
//randomize update timer so all oxygen generators don't update at the same time
|
||||
ventUpdateTimer = Rand.Range(0.0f, VentUpdateInterval);
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
@@ -78,6 +80,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private void GetVents()
|
||||
{
|
||||
totalHullVolume = 0.0f;
|
||||
ventList ??= new List<(Vent vent, float hullVolume)>();
|
||||
ventList.Clear();
|
||||
foreach (MapEntity entity in item.linkedTo)
|
||||
@@ -87,13 +90,14 @@ namespace Barotrauma.Items.Components
|
||||
Vent vent = linkedItem.GetComponent<Vent>();
|
||||
if (vent?.Item.CurrentHull == null) { continue; }
|
||||
|
||||
totalHullVolume += vent.Item.CurrentHull.Volume;
|
||||
ventList.Add((vent, vent.Item.CurrentHull.Volume));
|
||||
}
|
||||
|
||||
for (int i = 0; i < ventList.Count; i++)
|
||||
{
|
||||
Vent vent = ventList[i].vent;
|
||||
foreach (Hull connectedHull in vent.Item.CurrentHull.GetConnectedHulls(includingThis: false, searchDepth: 5, ignoreClosedGaps: true))
|
||||
foreach (Hull connectedHull in vent.Item.CurrentHull.GetConnectedHulls(includingThis: false, searchDepth: 3, ignoreClosedGaps: true))
|
||||
{
|
||||
//another vent in the connected hull -> don't add it to this vent's total hull volume
|
||||
if (ventList.Any(v => v.vent != vent && v.vent.Item.CurrentHull == connectedHull)) { continue; }
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CanBePut(ItemPrefab itemPrefab, float? condition = null)
|
||||
public bool CanBePut(ItemPrefab itemPrefab, float? condition = null, int? quality = null)
|
||||
{
|
||||
if (itemPrefab == null) { return false; }
|
||||
if (items.Count > 0)
|
||||
@@ -82,6 +82,11 @@ namespace Barotrauma
|
||||
if (items.Any(it => !it.IsFullCondition)) { return false; }
|
||||
}
|
||||
|
||||
if (quality.HasValue)
|
||||
{
|
||||
if (items[0].Quality != quality.Value) { return false; }
|
||||
}
|
||||
|
||||
if (items[0].Prefab.Identifier != itemPrefab.Identifier ||
|
||||
items.Count + 1 > itemPrefab.MaxStackSize)
|
||||
{
|
||||
@@ -172,6 +177,11 @@ namespace Barotrauma
|
||||
items.Clear();
|
||||
}
|
||||
|
||||
public void RemoveWhere(Func<Item, bool> predicate)
|
||||
{
|
||||
items.RemoveAll(it => predicate(it));
|
||||
}
|
||||
|
||||
public bool Any()
|
||||
{
|
||||
return items.Count > 0;
|
||||
@@ -422,16 +432,16 @@ namespace Barotrauma
|
||||
return slots[i].CanBePut(item, ignoreCondition);
|
||||
}
|
||||
|
||||
public bool CanBePut(ItemPrefab itemPrefab, float? condition = null)
|
||||
public bool CanBePut(ItemPrefab itemPrefab, float? condition = null, int? quality = null)
|
||||
{
|
||||
for (int i = 0; i < capacity; i++)
|
||||
{
|
||||
if (CanBePutInSlot(itemPrefab, i, condition)) { return true; }
|
||||
if (CanBePutInSlot(itemPrefab, i, condition, quality)) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool CanBePutInSlot(ItemPrefab itemPrefab, int i, float? condition = null)
|
||||
public virtual bool CanBePutInSlot(ItemPrefab itemPrefab, int i, float? condition = null, int? quality = null)
|
||||
{
|
||||
if (i < 0 || i >= slots.Length) { return false; }
|
||||
return slots[i].CanBePut(itemPrefab, condition);
|
||||
@@ -503,7 +513,7 @@ namespace Barotrauma
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return
|
||||
return
|
||||
TrySwapping(i, item, user, createNetworkEvent, swapWholeStack: true) ||
|
||||
TrySwapping(i, item, user, createNetworkEvent, swapWholeStack: false);
|
||||
}
|
||||
@@ -776,11 +786,17 @@ namespace Barotrauma
|
||||
{
|
||||
for (int j = 0; j < capacity; j++)
|
||||
{
|
||||
if (slots[j].Contains(item)) { slots[j].RemoveAllItems(); };
|
||||
if (slots[j].Contains(item))
|
||||
{
|
||||
slots[j].RemoveWhere(it => existingItems.Contains(it) || stackedItems.Contains(it));
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < otherInventory.capacity; j++)
|
||||
{
|
||||
if (otherInventory.slots[j].Contains(existingItems.FirstOrDefault())) { otherInventory.slots[j].RemoveAllItems(); }
|
||||
if (otherInventory.slots[j].Contains(existingItems.FirstOrDefault()))
|
||||
{
|
||||
otherInventory.slots[j].RemoveWhere(it => existingItems.Contains(it) || stackedItems.Contains(it));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,11 +51,11 @@ namespace Barotrauma
|
||||
return item != null && slots[i].CanBePut(item, ignoreCondition) && slots[i].ItemCount < container.GetMaxStackSize(i);
|
||||
}
|
||||
|
||||
public override bool CanBePutInSlot(ItemPrefab itemPrefab, int i, float? condition)
|
||||
public override bool CanBePutInSlot(ItemPrefab itemPrefab, int i, float? condition, int? quality = null)
|
||||
{
|
||||
if (i < 0 || i >= slots.Length) { return false; }
|
||||
if (!container.CanBeContained(itemPrefab, i)) { return false; }
|
||||
return itemPrefab != null && slots[i].CanBePut(itemPrefab, condition) && slots[i].ItemCount < container.GetMaxStackSize(i);
|
||||
return itemPrefab != null && slots[i].CanBePut(itemPrefab, condition, quality) && slots[i].ItemCount < container.GetMaxStackSize(i);
|
||||
}
|
||||
|
||||
public override int HowManyCanBePut(ItemPrefab itemPrefab, int i, float? condition)
|
||||
|
||||
@@ -864,6 +864,16 @@ namespace Barotrauma
|
||||
foreach (AbyssIsland island in AbyssIslands)
|
||||
{
|
||||
island.Area = new Rectangle(borders.Width - island.Area.Right, island.Area.Y, island.Area.Width, island.Area.Height);
|
||||
foreach (var cell in island.Cells)
|
||||
{
|
||||
if (!mirroredSites.Contains(cell.Site))
|
||||
{
|
||||
if (cell.Site.Coord.X % GridCellSize < 1.0f &&
|
||||
cell.Site.Coord.X % GridCellSize >= 0.0f) { cell.Site.Coord.X += 1.0f; }
|
||||
cell.Site.Coord.X = borders.Width - cell.Site.Coord.X;
|
||||
mirroredSites.Add(cell.Site);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < ruinPositions.Count; i++)
|
||||
@@ -1972,7 +1982,7 @@ namespace Barotrauma
|
||||
ConvertUnits.ToSimUnits(entranceWayPoint.WorldPosition), collisionCategory: Physics.CollisionLevel | Physics.CollisionWall) == null;
|
||||
});
|
||||
if (closestWp == null) { continue; }
|
||||
entranceWayPoint.ConnectTo(closestWp);
|
||||
ConnectWaypoints(entranceWayPoint, closestWp, outSideWaypointInterval);
|
||||
}
|
||||
|
||||
//create a waypoint path from the ruin to the closest tunnel
|
||||
|
||||
@@ -1023,7 +1023,12 @@ namespace Barotrauma
|
||||
#if CLIENT
|
||||
if (playSound && damageAmount > 0)
|
||||
{
|
||||
SoundPlayer.PlayDamageSound(attack.StructureSoundType, damageAmount, worldPosition, tags: Tags);
|
||||
string damageSound = Prefab.DamageSound;
|
||||
if (string.IsNullOrWhiteSpace(damageSound))
|
||||
{
|
||||
damageSound = attack.StructureSoundType;
|
||||
}
|
||||
SoundPlayer.PlayDamageSound(damageSound, damageAmount, worldPosition, tags: Tags);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -167,6 +167,9 @@ namespace Barotrauma
|
||||
private set { size = value; }
|
||||
}
|
||||
|
||||
[Serialize("", true)]
|
||||
public string DamageSound { get; private set; }
|
||||
|
||||
public Vector2 ScaledSize => size * Scale;
|
||||
|
||||
protected Vector2 textureScale = Vector2.One;
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace Barotrauma.Networking
|
||||
UpdateExperience,
|
||||
UpdateTalents,
|
||||
UpdateMoney,
|
||||
UpdatePermanentStats,
|
||||
}
|
||||
|
||||
public readonly Entity Entity;
|
||||
|
||||
@@ -1284,68 +1284,66 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
foreach (int giveExperience in giveExperiences)
|
||||
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
|
||||
{
|
||||
Character targetCharacter = CharacterFromTarget(target);
|
||||
if (targetCharacter != null && !targetCharacter.Removed)
|
||||
{
|
||||
targetCharacter?.Info?.GiveExperience(giveExperience);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
// these effects do not need to be run clientside, as they are replicated from server to clients anyway
|
||||
|
||||
if (giveSkills.Any())
|
||||
{
|
||||
foreach ((string skillIdentifier, float amount) in giveSkills)
|
||||
foreach (int giveExperience in giveExperiences)
|
||||
{
|
||||
Character targetCharacter = CharacterFromTarget(target);
|
||||
if (targetCharacter != null && !targetCharacter.Removed)
|
||||
{
|
||||
if (skillIdentifier?.ToLowerInvariant() == "randomskill")
|
||||
targetCharacter?.Info?.GiveExperience(giveExperience);
|
||||
}
|
||||
}
|
||||
|
||||
if (giveSkills.Any())
|
||||
{
|
||||
foreach ((string skillIdentifier, float amount) in giveSkills)
|
||||
{
|
||||
Character targetCharacter = CharacterFromTarget(target);
|
||||
if (targetCharacter != null && !targetCharacter.Removed)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
|
||||
if (skillIdentifier?.ToLowerInvariant() == "randomskill")
|
||||
{
|
||||
// don't let clients simulate random skill gain
|
||||
continue;
|
||||
targetCharacter.Info?.IncreaseSkillLevel(GetRandomSkill(), amount);
|
||||
|
||||
string GetRandomSkill()
|
||||
{
|
||||
return targetCharacter.Info?.Job?.Skills.Select(s => s.Identifier).GetRandom();
|
||||
}
|
||||
}
|
||||
targetCharacter.Info?.IncreaseSkillLevel(GetRandomSkill(), amount);
|
||||
|
||||
string GetRandomSkill()
|
||||
else
|
||||
{
|
||||
return targetCharacter.Info?.Job?.Skills.Select(s => s.Identifier).GetRandom();
|
||||
targetCharacter.Info?.IncreaseSkillLevel(skillIdentifier?.ToLowerInvariant(), amount);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
targetCharacter.Info?.IncreaseSkillLevel(skillIdentifier?.ToLowerInvariant(), amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (giveTalentInfos.Any() && (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient))
|
||||
{
|
||||
Character targetCharacter = CharacterFromTarget(target);
|
||||
if (targetCharacter?.Info == null) { continue; }
|
||||
if (!TalentTree.JobTalentTrees.TryGetValue(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<string> disallowedTalents = talentTree.TalentSubTrees.SelectMany(s => s.TalentOptionStages.SelectMany(o => o.Talents.Select(t => t.Identifier)));
|
||||
|
||||
foreach (GiveTalentInfo giveTalentInfo in giveTalentInfos)
|
||||
{
|
||||
IEnumerable<string> viableTalents = giveTalentInfo.TalentIdentifiers.Where(s => !targetCharacter.Info.UnlockedTalents.Contains(s) && !disallowedTalents.Contains(s));
|
||||
if (viableTalents.None()) { continue; }
|
||||
if (giveTalentInfos.Any())
|
||||
{
|
||||
Character targetCharacter = CharacterFromTarget(target);
|
||||
if (targetCharacter?.Info == null) { continue; }
|
||||
if (!TalentTree.JobTalentTrees.TryGetValue(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<string> disallowedTalents = talentTree.TalentSubTrees.SelectMany(s => s.TalentOptionStages.SelectMany(o => o.Talents.Select(t => t.Identifier)));
|
||||
|
||||
if (giveTalentInfo.GiveRandom)
|
||||
foreach (GiveTalentInfo giveTalentInfo in giveTalentInfos)
|
||||
{
|
||||
targetCharacter.GiveTalent(viableTalents.GetRandom(), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (string talent in viableTalents)
|
||||
IEnumerable<string> viableTalents = giveTalentInfo.TalentIdentifiers.Where(s => !targetCharacter.Info.UnlockedTalents.Contains(s) && !disallowedTalents.Contains(s));
|
||||
if (viableTalents.None()) { continue; }
|
||||
|
||||
if (giveTalentInfo.GiveRandom)
|
||||
{
|
||||
targetCharacter.GiveTalent(talent, true);
|
||||
targetCharacter.GiveTalent(viableTalents.GetRandom(), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (string talent in viableTalents)
|
||||
{
|
||||
targetCharacter.GiveTalent(talent, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,38 @@
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v0.1500.8.0
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
Additions and changes:
|
||||
- More improvements and fixes to the alien ruins and the new fractal guardians.
|
||||
- Improvements to character animations and sprites.
|
||||
- Made ruin scan/clear missions available in outposts.
|
||||
- Talent adjustments and fixes.
|
||||
- Adjustments to outpost distribution: natural formations greatly reduced in the 1st zone, cities slightly reduced in the 1st zone, outposts (including specialized ones) increased in the 3rd and 4th zone.
|
||||
- Made magnesium a little more common in stores and wrecks.
|
||||
- Temporarily disabled magnesium exploding in water to prevent issues with talents related to it.
|
||||
- Added an additonal ambience track for the ruins.
|
||||
- New sounds for alien ruins and the guardians.
|
||||
- Portable pumps turn off automatically when not attached to a wall.
|
||||
|
||||
Fixes:
|
||||
- Fixed outpost events always unlocking the same escort mission.
|
||||
- Fixed server occasionally failing to end the round and spamming the clients with XP notifications. Happened when the server was trying to give experience for the completed missions and there were clients in the server whose character had been removed (unstable only).
|
||||
- Fixed shotgun shells not having a fabrication recipe (unstable only).
|
||||
- Fixed thermal goggles being sold in outposts (unstable only).
|
||||
- Fixes to ruin waypoint generation (unstable only).
|
||||
- Fixes to pathfinding outside ruins (unstable only).
|
||||
- Fixed oxygen generator output constantly decreasing due to the changes in the previous build (unstable only).
|
||||
- Fixed long item names being unreadable on the fabricator UI.
|
||||
- The hints about flooded rooms and ballast flora aren't shown in ruins, wrecks or enemy subs.
|
||||
- Fixed "setclientcharacter" command crashing the server if the specified character is not found.
|
||||
- Fixed autofilling subs with supplies sometimes causing high-quality items to appear on the floor near containers (unstable only).
|
||||
- Fixed guardian spears being fabricatable (unstable only).
|
||||
- Fixed "stowaway" event triggering an event cooldown, preventing monsters from spawning at the beginning of the round.
|
||||
- Fixed clients (excluding the host) always considering friendly fire to be disabled, leading to minor cosmetic desyncs when a player applies afflictions on another one (i.e. there was a brief delay before the afflictions update client-side).
|
||||
- Fixed inability to apply buffs on the crew when friendly fire is disabled.
|
||||
- Fixed ItemContainers only applying the StatusEffects from the first matching Containable, even if there's multiple. Prevented the artifact-specific effects of artifact holder from executing.
|
||||
- Fixed "giveaffliction" command's limbtype argument not working in multiplayer.
|
||||
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v0.1500.7.0
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
@@ -7,7 +42,7 @@ Additions and changes:
|
||||
- Added a colored border to high-quality items' inventory slots.
|
||||
- Changed the look of the skill/xp notifications to accommodate the larger numbers of notifications you can get from talents and skillbooks.
|
||||
- Added a fabricator and deconstructor to Azimuth and slightly lowered its maximum speed.
|
||||
- Increased Azimuth's battery out relay max power-
|
||||
- Increased Azimuth's battery out relay max power.
|
||||
- Field Medic now only triggers on missions.
|
||||
- Reduced gravity sphere's force to make it possible to escape it with a diving suit on.
|
||||
- Diving suit and human ragdoll damagemodifier changes: the suits now offer less protection, but humans have a bit more natural protection towards physical damage types.
|
||||
|
||||
Reference in New Issue
Block a user