b48aa89004
commit e6715d605db9bb1d608e4a4990ac41f6214f61d1 Author: Joonas Rikkonen <poe.regalis@gmail.com> Date: Tue Mar 5 17:22:31 2019 +0200 Fixed the "control" console command not being usable by clients, changed the way arguments are given to the "setclientcharacter" command (no semicolon to separate the names, quotation marks have to be used for multi-word names just like with any other command). Closes #1224 commit acb7c1e0dc05bf619e7ec4875196cc45647d3fb4 Author: Joonas Rikkonen <poe.regalis@gmail.com> Date: Tue Mar 5 16:30:38 2019 +0200 Steam Workshop fixes: - Install content packages for items that only contain a sub. Otherwise the system can't determine if the item has been updated (and might also be useful for toggling off incompatible subs when switching from mod to another). - Log an error instead of crashing the game if CheckWorkshopItemEnabled or CheckWorkshopItemUpToDate fails due to a missing filelist.xml. - Show a messagebox when a workshop item is updated succesfully. commit b2e8ed565bc03f466930799268c13b2fca4bb9c9 Author: Joonas Rikkonen <poe.regalis@gmail.com> Date: Tue Mar 5 14:43:42 2019 +0200 Fixed nullref exception when disabling a workshop item that doesn't have an update button (or when enabling the item fails) commit 26f1f285cd80ca6f023b12e6dd80dc71e87ee9c3 Author: Joonas Rikkonen <poe.regalis@gmail.com> Date: Tue Mar 5 14:15:14 2019 +0200 Fixed console command aliases not being taken into account in GameClient.HasConsoleCommandPermission (meaning that the client needed a permission for each name variant of a command, making it impossible to for example use "fixwalls" instead of "fixhulls"). Closes #1225 commit dee02de681a212efd0e0a82c14619f3fe4839cc4 Author: Joonas Rikkonen <poe.regalis@gmail.com> Date: Tue Mar 5 13:35:48 2019 +0200 Fixed traitor rounds failing to start if there's no owner client, fixed occasional "traitorCount somehow ended up less than 1" errors due to Rand.Int using 0 as the minimum value. Closes #1217
392 lines
12 KiB
C#
392 lines
12 KiB
C#
using Microsoft.Xna.Framework;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Xml.Linq;
|
|
|
|
namespace Barotrauma.Items.Components
|
|
{
|
|
partial class ItemContainer : ItemComponent, IDrawableComponent
|
|
{
|
|
private List<RelatedItem> containableItems;
|
|
public ItemInventory Inventory;
|
|
|
|
private List<Pair<Item, StatusEffect>> itemsWithStatusEffects;
|
|
|
|
private ushort[] itemIds;
|
|
|
|
//how many items can be contained
|
|
private int capacity;
|
|
[Serialize(5, false)]
|
|
public int Capacity
|
|
{
|
|
get { return capacity; }
|
|
set { capacity = Math.Max(value, 1); }
|
|
}
|
|
|
|
private bool hideItems;
|
|
[Serialize(true, false)]
|
|
public bool HideItems
|
|
{
|
|
get { return hideItems; }
|
|
set
|
|
{
|
|
hideItems = value;
|
|
Drawable = !hideItems;
|
|
}
|
|
}
|
|
|
|
[Serialize(true, false)]
|
|
public bool DrawInventory
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
|
|
[Serialize(false, false)]
|
|
public bool AutoInteractWithContained
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
[Serialize("0.5,0.5", false)]
|
|
public Vector2 HudPos { get; set; }
|
|
[Serialize(5, false)]
|
|
public int SlotsPerRow { get; set; }
|
|
|
|
public List<RelatedItem> ContainableItems
|
|
{
|
|
get { return containableItems; }
|
|
}
|
|
|
|
public ItemContainer(Item item, XElement element)
|
|
: base (item, element)
|
|
{
|
|
Inventory = new ItemInventory(item, this, capacity, HudPos, SlotsPerRow);
|
|
containableItems = new List<RelatedItem>();
|
|
|
|
foreach (XElement subElement in element.Elements())
|
|
{
|
|
switch (subElement.Name.ToString().ToLowerInvariant())
|
|
{
|
|
case "containable":
|
|
RelatedItem containable = RelatedItem.Load(subElement, item.Name);
|
|
if (containable == null)
|
|
{
|
|
DebugConsole.ThrowError("Error in item config \"" + item.ConfigFile + "\" - containable with no identifiers.");
|
|
continue;
|
|
}
|
|
containableItems.Add(containable);
|
|
break;
|
|
}
|
|
}
|
|
|
|
InitProjSpecific(element);
|
|
|
|
itemsWithStatusEffects = new List<Pair<Item, StatusEffect>>();
|
|
}
|
|
|
|
partial void InitProjSpecific(XElement element);
|
|
|
|
public void OnItemContained(Item containedItem)
|
|
{
|
|
item.SetContainedItemPositions();
|
|
|
|
RelatedItem ri = containableItems.Find(x => x.MatchesItem(containedItem));
|
|
if (ri != null)
|
|
{
|
|
foreach (StatusEffect effect in ri.statusEffects)
|
|
{
|
|
itemsWithStatusEffects.Add(new Pair<Item, StatusEffect>(containedItem, effect));
|
|
}
|
|
}
|
|
|
|
//no need to Update() if this item has no statuseffects and no physics body
|
|
IsActive = itemsWithStatusEffects.Count > 0 || containedItem.body != null;
|
|
}
|
|
|
|
public void OnItemRemoved(Item item)
|
|
{
|
|
itemsWithStatusEffects.RemoveAll(i => i.First == item);
|
|
|
|
//deactivate if the inventory is empty
|
|
IsActive = itemsWithStatusEffects.Count > 0 || item.body != null;
|
|
}
|
|
|
|
public bool CanBeContained(Item item)
|
|
{
|
|
if (containableItems.Count == 0) return true;
|
|
return (containableItems.Find(x => x.MatchesItem(item)) != null);
|
|
}
|
|
|
|
public override void Update(float deltaTime, Camera cam)
|
|
{
|
|
if (item.body != null &&
|
|
item.body.Enabled &&
|
|
item.body.FarseerBody.Awake)
|
|
{
|
|
item.SetContainedItemPositions();
|
|
}
|
|
|
|
foreach (Pair<Item, StatusEffect> itemAndEffect in itemsWithStatusEffects)
|
|
{
|
|
Item contained = itemAndEffect.First;
|
|
if (contained.Condition <= 0.0f) continue;
|
|
|
|
StatusEffect effect = itemAndEffect.Second;
|
|
|
|
if (effect.HasTargetType(StatusEffect.TargetType.This))
|
|
effect.Apply(ActionType.OnContaining, deltaTime, item, item.AllPropertyObjects);
|
|
if (effect.HasTargetType(StatusEffect.TargetType.Contained))
|
|
effect.Apply(ActionType.OnContaining, deltaTime, item, contained.AllPropertyObjects);
|
|
if (effect.HasTargetType(StatusEffect.TargetType.NearbyItems) ||
|
|
effect.HasTargetType(StatusEffect.TargetType.NearbyCharacters))
|
|
{
|
|
var targets = new List<ISerializableEntity>();
|
|
effect.GetNearbyTargets(item.WorldPosition, targets);
|
|
effect.Apply(ActionType.OnActive, deltaTime, item, targets);
|
|
}
|
|
}
|
|
}
|
|
|
|
public override bool Select(Character character)
|
|
{
|
|
if (item.Container != null) { return false; }
|
|
|
|
if (AutoInteractWithContained)
|
|
{
|
|
foreach (Item contained in Inventory.Items)
|
|
{
|
|
if (contained == null) continue;
|
|
if (contained.TryInteract(character))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return base.Select(character);
|
|
}
|
|
|
|
public override bool Select(Character character)
|
|
{
|
|
if (item.Container != null) { return false; }
|
|
|
|
if (AutoInteractWithContained)
|
|
{
|
|
foreach (Item contained in Inventory.Items)
|
|
{
|
|
if (contained == null) continue;
|
|
if (contained.TryInteract(character))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return base.Select(character);
|
|
}
|
|
|
|
public override bool Select(Character character)
|
|
{
|
|
if (item.Container != null) { return false; }
|
|
|
|
if (AutoInteractWithContained)
|
|
{
|
|
foreach (Item contained in Inventory.Items)
|
|
{
|
|
if (contained == null) continue;
|
|
if (contained.TryInteract(character))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return base.Select(character);
|
|
}
|
|
|
|
public override bool Select(Character character)
|
|
{
|
|
if (item.Container != null) { return false; }
|
|
|
|
if (AutoInteractWithContained)
|
|
{
|
|
foreach (Item contained in Inventory.Items)
|
|
{
|
|
if (contained == null) continue;
|
|
if (contained.TryInteract(character))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return base.Select(character);
|
|
}
|
|
|
|
public override bool Select(Character character)
|
|
{
|
|
if (item.Container != null) { return false; }
|
|
|
|
if (AutoInteractWithContained)
|
|
{
|
|
foreach (Item contained in Inventory.Items)
|
|
{
|
|
if (contained == null) continue;
|
|
if (contained.TryInteract(character))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return base.Select(character);
|
|
}
|
|
|
|
public override bool Pick(Character picker)
|
|
{
|
|
if (AutoInteractWithContained)
|
|
{
|
|
foreach (Item contained in Inventory.Items)
|
|
{
|
|
if (contained == null) continue;
|
|
if (contained.TryInteract(picker))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (picker != null);
|
|
}
|
|
|
|
public override bool Combine(Item item)
|
|
{
|
|
if (!containableItems.Any(x => x.MatchesItem(item))) return false;
|
|
|
|
if (Inventory.TryPutItem(item, null))
|
|
{
|
|
IsActive = true;
|
|
if (hideItems && item.body != null) item.body.Enabled = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public void SetContainedItemPositions()
|
|
{
|
|
Vector2 simPos = item.SimPosition;
|
|
Vector2 displayPos = item.Position;
|
|
|
|
foreach (Item contained in Inventory.Items)
|
|
{
|
|
if (contained == null) continue;
|
|
if (contained.body != null)
|
|
{
|
|
try
|
|
{
|
|
contained.body.FarseerBody.SetTransformIgnoreContacts(ref simPos, 0.0f);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
DebugConsole.Log("SetTransformIgnoreContacts threw an exception in SetContainedItemPositions ("+e.Message+")\n"+e.StackTrace);
|
|
GameAnalyticsManager.AddErrorEventOnce("ItemContainer.SetContainedItemPositions.InvalidPosition:"+contained.Name,
|
|
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
|
"SetTransformIgnoreContacts threw an exception in SetContainedItemPositions (" + e.Message + ")\n" + e.StackTrace);
|
|
}
|
|
}
|
|
|
|
contained.Rect =
|
|
new Rectangle(
|
|
(int)(displayPos.X - contained.Rect.Width / 2.0f),
|
|
(int)(displayPos.Y + contained.Rect.Height / 2.0f),
|
|
contained.Rect.Width, contained.Rect.Height);
|
|
|
|
contained.Submarine = item.Submarine;
|
|
contained.CurrentHull = item.CurrentHull;
|
|
|
|
contained.SetContainedItemPositions();
|
|
}
|
|
}
|
|
|
|
public override void OnMapLoaded()
|
|
{
|
|
if (itemIds == null) return;
|
|
|
|
for (ushort i = 0; i < itemIds.Length; i++)
|
|
{
|
|
Item item = Entity.FindEntityByID(itemIds[i]) as Item;
|
|
if (item == null) continue;
|
|
|
|
if (i >= Inventory.Capacity)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Inventory.TryPutItem(item, i, false, false, null, false);
|
|
}
|
|
|
|
itemIds = null;
|
|
}
|
|
|
|
protected override void ShallowRemoveComponentSpecific()
|
|
{
|
|
}
|
|
|
|
protected override void RemoveComponentSpecific()
|
|
{
|
|
#if CLIENT
|
|
inventoryTopSprite?.Remove();
|
|
inventoryBackSprite?.Remove();
|
|
inventoryBottomSprite?.Remove();
|
|
ContainedStateIndicator?.Remove();
|
|
|
|
if (Screen.Selected == GameMain.SubEditorScreen && !Submarine.Unloading)
|
|
{
|
|
GameMain.SubEditorScreen.HandleContainerContentsDeletion(Item, Inventory);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
foreach (Item item in Inventory.Items)
|
|
{
|
|
if (item == null) continue;
|
|
item.Drop();
|
|
}
|
|
}
|
|
|
|
public override void Load(XElement componentElement)
|
|
{
|
|
base.Load(componentElement);
|
|
|
|
string containedString = componentElement.GetAttributeString("contained", "");
|
|
|
|
string[] itemIdStrings = containedString.Split(',');
|
|
|
|
itemIds = new ushort[itemIdStrings.Length];
|
|
for (int i = 0; i < itemIdStrings.Length; i++)
|
|
{
|
|
ushort id = 0;
|
|
if (!ushort.TryParse(itemIdStrings[i], out id)) continue;
|
|
|
|
itemIds[i] = id;
|
|
}
|
|
}
|
|
|
|
public override XElement Save(XElement parentElement)
|
|
{
|
|
XElement componentElement = base.Save(parentElement);
|
|
|
|
string[] itemIdStrings = new string[Inventory.Items.Length];
|
|
for (int i = 0; i < Inventory.Items.Length; i++)
|
|
{
|
|
itemIdStrings[i] = (Inventory.Items[i] == null) ? "0" : Inventory.Items[i].ID.ToString();
|
|
}
|
|
|
|
componentElement.Add(new XAttribute("contained", string.Join(",", itemIdStrings)));
|
|
|
|
return componentElement;
|
|
}
|
|
}
|
|
}
|