Updated and refactored how items and characters are chosen for interactions in the world. The new system has more precision, with items under the cursor taking priority (this helps with small items which are in front of large items). A new method CanInteractWith(Item) has been added to the Character class which does all validation of interaction with items, including new distance calculations based on bounding boxes. This gives more consistency in selecting and interacting with all sizes and shapes of items. Because of this pickdistance (not interactdistance) has been removed from all item configurations (in their XML config files) and the default InteractDistance for all items is now 120.
This commit is contained in:
@@ -105,9 +105,7 @@ namespace Barotrauma.Items.Components
|
||||
pickTimer = 0.0f;
|
||||
while (pickTimer < requiredTime && Screen.Selected != GameMain.EditMapScreen)
|
||||
{
|
||||
if (picker.IsKeyDown(InputType.Aim) ||
|
||||
!item.IsInPickRange(picker.WorldPosition) ||
|
||||
picker.Stun > 0.0f || picker.IsDead)
|
||||
if (picker.IsKeyDown(InputType.Aim) || !picker.CanInteractWith(item))
|
||||
{
|
||||
StopPicking(picker);
|
||||
yield return CoroutineStatus.Success;
|
||||
|
||||
@@ -77,10 +77,8 @@ namespace Barotrauma.Items.Components
|
||||
this.cam = cam;
|
||||
|
||||
if (character == null
|
||||
|| character.IsDead
|
||||
|| character.Stun > 0.0f
|
||||
|| character.SelectedConstruction != item
|
||||
|| Vector2.Distance(character.Position, item.Position) > item.PickDistance * 2.0f)
|
||||
|| !character.CanInteractWith(item))
|
||||
{
|
||||
if (character != null)
|
||||
{
|
||||
@@ -150,7 +148,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override bool Use(float deltaTime, Character activator = null)
|
||||
{
|
||||
if (character == null || activator != character || character.SelectedConstruction != item)
|
||||
if (character == null || activator != character || character.SelectedConstruction != item || !character.CanInteractWith(item))
|
||||
{
|
||||
character = null;
|
||||
return false;
|
||||
@@ -165,7 +163,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void SecondaryUse(float deltaTime, Character character = null)
|
||||
{
|
||||
if (this.character == null || this.character != character || this.character.SelectedConstruction != item)
|
||||
if (this.character == null || this.character != character || this.character.SelectedConstruction != item || !character.CanInteractWith(item))
|
||||
{
|
||||
character = null;
|
||||
return;
|
||||
|
||||
@@ -28,9 +28,9 @@ namespace Barotrauma.Items.Components
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight),
|
||||
Color.Green * 0.1f, true);
|
||||
|
||||
if (character.ClosestCharacter == null) return;
|
||||
if (character.FocusedCharacter == null) return;
|
||||
|
||||
var target = character.ClosestCharacter;
|
||||
var target = character.FocusedCharacter;
|
||||
|
||||
Vector2 hudPos = GameMain.GameScreen.Cam.WorldToScreen(target.WorldPosition);
|
||||
hudPos += Vector2.UnitX * 50.0f;
|
||||
|
||||
@@ -136,9 +136,14 @@ namespace Barotrauma
|
||||
get { return prefab.sprite; }
|
||||
}
|
||||
|
||||
public float PickDistance
|
||||
public float InteractDistance
|
||||
{
|
||||
get { return prefab.PickDistance; }
|
||||
get { return prefab.InteractDistance; }
|
||||
}
|
||||
|
||||
public float InteractPriority
|
||||
{
|
||||
get { return prefab.InteractPriority; }
|
||||
}
|
||||
|
||||
public override Vector2 SimPosition
|
||||
@@ -919,8 +924,7 @@ namespace Barotrauma
|
||||
|
||||
if (prefab.sprite != null)
|
||||
{
|
||||
float depth = Sprite.Depth;
|
||||
depth += (ID % 255) * 0.000001f;
|
||||
float depth = GetDrawDepth();
|
||||
|
||||
if (body == null)
|
||||
{
|
||||
@@ -975,7 +979,7 @@ namespace Barotrauma
|
||||
if (IsSelected || isHighlighted)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y+rect.Height/2)), new Vector2(rect.Width, rect.Height), Color.Green,false,0,(int)Math.Max((1.5f/GameScreen.Selected.Cam.Zoom),1.0f));
|
||||
|
||||
|
||||
foreach (Rectangle t in prefab.Triggers)
|
||||
{
|
||||
Rectangle transformedTrigger = TransformTrigger(t);
|
||||
@@ -1304,77 +1308,11 @@ namespace Barotrauma
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
public static Item FindPickable(Vector2 position, Vector2 pickPosition, Hull hull = null, Item[] ignoredItems = null)
|
||||
public float GetDrawDepth()
|
||||
{
|
||||
float dist;
|
||||
return FindPickable(position, pickPosition, hull, ignoredItems, out dist);
|
||||
}
|
||||
return Sprite.Depth + ((ID % 255) * 0.000001f);
|
||||
}
|
||||
|
||||
/// <param name="position">Position of the Character doing the pick, only items that are close enough to this are checked</param>
|
||||
/// <param name="pickPosition">the item closest to pickPosition is returned</param>
|
||||
/// <param name="hull">If a hull is specified, only items within that hull are checked</param>
|
||||
public static Item FindPickable(Vector2 position, Vector2 pickPosition, Hull hull, Item[] ignoredItems, out float distance)
|
||||
{
|
||||
float closestDist = 0.0f, dist;
|
||||
Item closest = null;
|
||||
|
||||
Vector2 displayPos = ConvertUnits.ToDisplayUnits(position);
|
||||
Vector2 displayPickPos = ConvertUnits.ToDisplayUnits(pickPosition);
|
||||
|
||||
distance = 1000.0f;
|
||||
|
||||
foreach (Item item in ItemList)
|
||||
{
|
||||
if (ignoredItems != null && ignoredItems.Contains(item)) continue;
|
||||
if (item.body != null && !item.body.Enabled) continue;
|
||||
|
||||
if (item.PickDistance == 0.0f && !item.prefab.Triggers.Any()) continue;
|
||||
|
||||
Pickable pickableComponent = item.GetComponent<Pickable>();
|
||||
if (pickableComponent != null && (pickableComponent.Picker != null && !pickableComponent.Picker.IsDead)) continue;
|
||||
|
||||
float pickDist = Vector2.Distance(item.WorldPosition, displayPickPos);
|
||||
|
||||
bool insideTrigger = false;
|
||||
foreach (Rectangle trigger in item.prefab.Triggers)
|
||||
{
|
||||
Rectangle transformedTrigger = item.TransformTrigger(trigger, true);
|
||||
|
||||
if (!Submarine.RectContains(transformedTrigger, displayPos)) continue;
|
||||
|
||||
insideTrigger = true;
|
||||
|
||||
Vector2 triggerCenter = new Vector2(transformedTrigger.Center.X, transformedTrigger.Y - transformedTrigger.Height / 2);
|
||||
pickDist = Math.Min(Math.Abs(triggerCenter.X - displayPickPos.X), Math.Abs(triggerCenter.Y - displayPickPos.Y));
|
||||
}
|
||||
|
||||
if (!insideTrigger && item.prefab.Triggers.Any()) continue;
|
||||
|
||||
if (pickDist > item.PickDistance && item.PickDistance > 0.0f) continue;
|
||||
|
||||
dist = item.Sprite.Depth * 10.0f + pickDist;
|
||||
if (item.IsMouseOn(displayPickPos)) dist = dist * 0.1f;
|
||||
|
||||
if (closest == null || dist < closestDist)
|
||||
{
|
||||
if (item.PickDistance > 0.0f && Vector2.Distance(displayPos, item.WorldPosition) > item.prefab.PickDistance) continue;
|
||||
|
||||
if (!item.prefab.PickThroughWalls && Screen.Selected != GameMain.EditMapScreen && !insideTrigger)
|
||||
{
|
||||
Body body = Submarine.CheckVisibility(item.Submarine == null ? position : position - item.Submarine.SimPosition, item.SimPosition, true);
|
||||
if (body != null && body.UserData as Item != item) continue;
|
||||
}
|
||||
|
||||
closestDist = dist;
|
||||
closest = item;
|
||||
|
||||
distance = pickDist;
|
||||
}
|
||||
}
|
||||
|
||||
return closest;
|
||||
}
|
||||
|
||||
public bool IsInsideTrigger(Vector2 worldPosition)
|
||||
{
|
||||
foreach (Rectangle trigger in prefab.Triggers)
|
||||
@@ -1387,26 +1325,19 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsInPickRange(Vector2 worldPosition)
|
||||
{
|
||||
if (IsInsideTrigger(worldPosition)) return true;
|
||||
|
||||
return Vector2.Distance(WorldPosition, worldPosition) < PickDistance;
|
||||
}
|
||||
|
||||
public bool CanClientAccess(Client c)
|
||||
{
|
||||
return c != null && c.Character != null && c.Character.CanAccessItem(this);
|
||||
return c != null && c.Character != null && c.Character.CanInteractWith(this);
|
||||
}
|
||||
|
||||
public bool Pick(Character picker, bool ignoreRequiredItems=false, bool forceSelectKey=false, bool forceActionKey=false)
|
||||
public bool TryInteract(Character picker, bool ignoreRequiredItems=false, bool forceSelectKey=false, bool forceActionKey=false)
|
||||
{
|
||||
bool hasRequiredSkills = true;
|
||||
|
||||
bool picked = false, selected = false;
|
||||
|
||||
Skill requiredSkill = null;
|
||||
|
||||
|
||||
foreach (ItemComponent ic in components)
|
||||
{
|
||||
bool pickHit = false, selectHit = false;
|
||||
@@ -1434,7 +1365,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!pickHit && !selectHit) continue;
|
||||
|
||||
Skill tempRequiredSkill;
|
||||
@@ -1444,7 +1374,7 @@ namespace Barotrauma
|
||||
|
||||
bool showUiMsg = picker == Character.Controlled && Screen.Selected != GameMain.EditMapScreen;
|
||||
if (!ignoreRequiredItems && !ic.HasRequiredItems(picker, showUiMsg)) continue;
|
||||
if ((ic.CanBePicked && pickHit && ic.Pick(picker)) ||
|
||||
if ((ic.CanBePicked && pickHit && ic.Pick(picker)) ||
|
||||
(ic.CanBeSelected && selectHit && ic.Select(picker)))
|
||||
{
|
||||
picked = true;
|
||||
@@ -1466,22 +1396,22 @@ namespace Barotrauma
|
||||
if (picker.IsKeyHit(InputType.Select) || forceSelectKey) picker.SelectedConstruction = null;
|
||||
}
|
||||
else if (selected)
|
||||
{
|
||||
{
|
||||
picker.SelectedConstruction = this;
|
||||
}
|
||||
|
||||
if (!hasRequiredSkills && Character.Controlled==picker && Screen.Selected != GameMain.EditMapScreen)
|
||||
|
||||
if (!hasRequiredSkills && Character.Controlled == picker && Screen.Selected != GameMain.EditMapScreen)
|
||||
{
|
||||
GUI.AddMessage("Your skills may be insufficient to use the item!", Color.Red, 5.0f);
|
||||
if (requiredSkill != null)
|
||||
{
|
||||
GUI.AddMessage("("+requiredSkill.Name+" level "+requiredSkill.Level+" required)", Color.Red, 5.0f);
|
||||
GUI.AddMessage("(" + requiredSkill.Name + " level " + requiredSkill.Level + " required)", Color.Red, 5.0f);
|
||||
}
|
||||
}
|
||||
|
||||
if (Container!=null) Container.RemoveContained(this);
|
||||
if (Container != null) Container.RemoveContained(this);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1853,7 +1783,7 @@ namespace Barotrauma
|
||||
int requirementIndex = FixRequirements.Count == 1 ?
|
||||
0 : msg.ReadRangedInteger(0, FixRequirements.Count - 1);
|
||||
|
||||
if (c.Character == null || !c.Character.CanAccessItem(this)) return;
|
||||
if (c.Character == null || !c.Character.CanInteractWith(this)) return;
|
||||
if (!FixRequirements[requirementIndex].CanBeFixed(c.Character)) return;
|
||||
|
||||
FixRequirements[requirementIndex].Fixed = true;
|
||||
@@ -1866,7 +1796,7 @@ namespace Barotrauma
|
||||
|
||||
break;
|
||||
case NetEntityEvent.Type.ApplyStatusEffect:
|
||||
if (c.Character == null || !c.Character.CanAccessItem(this)) return;
|
||||
if (c.Character == null || !c.Character.CanInteractWith(this)) return;
|
||||
|
||||
ApplyStatusEffects(ActionType.OnUse, (float)Timing.Step, c.Character);
|
||||
|
||||
|
||||
@@ -35,9 +35,11 @@ namespace Barotrauma
|
||||
protected Vector2 size;
|
||||
|
||||
//how close the Character has to be to the item to pick it up
|
||||
private float pickDistance;
|
||||
private float interactDistance;
|
||||
// this can be used to allow items which are behind other items tp
|
||||
private float interactPriority;
|
||||
|
||||
private bool pickThroughWalls;
|
||||
private bool interactThroughWalls;
|
||||
|
||||
//an area next to the construction
|
||||
//the construction can be Activated() by a Character inside the area
|
||||
@@ -80,14 +82,19 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
public float PickDistance
|
||||
public float InteractDistance
|
||||
{
|
||||
get { return pickDistance; }
|
||||
get { return interactDistance; }
|
||||
}
|
||||
|
||||
public bool PickThroughWalls
|
||||
public float InteractPriority
|
||||
{
|
||||
get { return pickThroughWalls; }
|
||||
get { return interactPriority; }
|
||||
}
|
||||
|
||||
public bool InteractThroughWalls
|
||||
{
|
||||
get { return interactThroughWalls; }
|
||||
}
|
||||
|
||||
public override bool IsLinkable
|
||||
@@ -268,9 +275,10 @@ namespace Barotrauma
|
||||
|
||||
Description = ToolBox.GetAttributeString(element, "description", "");
|
||||
|
||||
pickThroughWalls = ToolBox.GetAttributeBool(element, "pickthroughwalls", false);
|
||||
pickDistance = ToolBox.GetAttributeFloat(element, "pickdistance", 0.0f);
|
||||
|
||||
interactThroughWalls = ToolBox.GetAttributeBool(element, "interactthroughwalls", false);
|
||||
interactDistance = ToolBox.GetAttributeFloat(element, "interactdistance", 120.0f); // Default to 120 as the new item picking method is tuned to this number
|
||||
interactPriority = ToolBox.GetAttributeFloat(element, "interactpriority", 0.0f);
|
||||
|
||||
isLinkable = ToolBox.GetAttributeBool(element, "linkable", false);
|
||||
|
||||
resizeHorizontal = ToolBox.GetAttributeBool(element, "resizehorizontal", false);
|
||||
|
||||
Reference in New Issue
Block a user