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:
Faerdan
2017-06-18 22:30:54 +01:00
parent 5cd2a5a838
commit a09367c78f
45 changed files with 483 additions and 476 deletions
@@ -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;
+24 -94
View File
@@ -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);
+17 -9
View File
@@ -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);