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:
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user