2 using Microsoft.Xna.Framework;
4 using System.Collections.Generic;
13 private float rangeX, rangeY;
15 private Vector2 detectOffset;
17 private float updateTimer;
29 [
Serialize(
false,
IsPropertySaveable.No, description:
"Has the item currently detected movement. Intended to be used by StatusEffect conditionals (setting this value in XML has no effect).")]
39 [
Editable,
Serialize(
"",
IsPropertySaveable.Yes, description:
"Does the sensor react only to certain characters (species names, groups or tags)? Doesn't have an effect, if the Target Type is incorrect.", alwaysUseInstanceValues:
true)]
42 get => targetCharacters.ConvertToString();
43 set => targetCharacters = value.ToIdentifiers().ToHashSet();
45 private HashSet<Identifier> targetCharacters;
57 get {
return rangeX; }
60 rangeX = MathHelper.Clamp(value, 0.0f, 1000.0f);
62 item.ResetCachedVisibleSize();
69 get {
return rangeY; }
72 rangeY = MathHelper.Clamp(value, 0.0f, 1000.0f);
76 [
InGameEditable,
Serialize(
"0,0",
IsPropertySaveable.Yes, description:
"The position to detect the movement at relative to the item. For example, 0,100 would detect movement 100 units above the item.")]
79 get {
return detectOffset; }
83 detectOffset.X = MathHelper.Clamp(value.X, -rangeX, rangeX);
84 detectOffset.Y = MathHelper.Clamp(value.Y, -rangeY, rangeY);
92 Vector2 transformedDetectOffset = detectOffset;
93 if (
item.
FlippedX) { transformedDetectOffset.X = -transformedDetectOffset.X; }
94 if (
item.
FlippedY) { transformedDetectOffset.Y = -transformedDetectOffset.Y; }
95 return transformedDetectOffset;
99 [
Editable(MinValueFloat = 0.1f, MaxValueFloat = 100.0f, DecimalCount = 2),
Serialize(0.1f,
IsPropertySaveable.Yes, description:
"How often the sensor checks if there's something moving near it. Higher values are better for performance.", alwaysUseInstanceValues:
true)]
106 private int maxOutputLength;
107 [
Editable,
Serialize(200,
IsPropertySaveable.No, description:
"The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")]
110 get {
return maxOutputLength; }
113 maxOutputLength = Math.Max(value, 0);
117 private string output;
121 get {
return output; }
124 if (value ==
null) {
return; }
133 private string falseOutput;
137 get {
return falseOutput; }
140 if (value ==
null) {
return; }
164 : base(
item, element)
180 base.Load(componentElement, usePrefabValues, idRemap, isItemSwap);
192 if (!
string.IsNullOrEmpty(signalOut)) {
item.
SendSignal(
new Signal(signalOut, 1),
"state_out"); }
199 updateTimer -= deltaTime;
200 if (updateTimer > 0.0f) {
return; }
215 Rectangle detectRect =
new Rectangle((
int)(detectPos.X - rangeX), (
int)(detectPos.Y - rangeY), (
int)(rangeX * 2), (
int)(rangeY * 2));
216 float broadRangeX = Math.Max(rangeX * 2, 500);
217 float broadRangeY = Math.Max(rangeY * 2, 500);
224 foreach (var cell
in cells)
231 foreach (var edge
in cell.Edges)
233 Vector2 e1 = edge.Point1 + cell.Translation;
234 Vector2 e2 = edge.Point2 + cell.Translation;
235 if (MathUtils.LineSegmentsIntersect(e1, e2,
new Vector2(detectRect.X, detectRect.Y),
new Vector2(detectRect.Right, detectRect.Y)) ||
236 MathUtils.LineSegmentsIntersect(e1, e2,
new Vector2(detectRect.X, detectRect.Bottom),
new Vector2(detectRect.Right, detectRect.Bottom)) ||
237 MathUtils.LineSegmentsIntersect(e1, e2,
new Vector2(detectRect.X, detectRect.Y),
new Vector2(detectRect.X, detectRect.Bottom)) ||
238 MathUtils.LineSegmentsIntersect(e1, e2,
new Vector2(detectRect.Right, detectRect.Y),
new Vector2(detectRect.Right, detectRect.Bottom)))
253 Rectangle worldBorders =
new Rectangle(
259 if (worldBorders.Intersects(detectRect))
270 bool hasTriggers = triggerFromHumans || triggerFromPets || triggerFromMonsters;
271 if (!hasTriggers) {
return; }
276 if (character.
SpawnTime > Timing.TotalTime - 1.0) {
continue; }
282 if (Math.Abs(character.
WorldPosition.X - detectPos.X) > broadRangeX || Math.Abs(character.
WorldPosition.Y - detectPos.Y) > broadRangeY)
305 bool hasTriggers = triggerFromHumans || triggerFromPets || triggerFromMonsters;
306 if (!hasTriggers) {
return false; }
307 return TriggersOn(character, triggerFromHumans, triggerFromPets, triggerFromMonsters);
310 private bool TriggersOn(
Character character,
bool triggerFromHumans,
bool triggerFromPets,
bool triggerFromMonsters)
315 if (!triggerFromHumans) {
return false; }
317 else if (character.
IsPet)
319 if (!triggerFromPets) {
return false; }
324 if (!triggerFromMonsters) {
return false; }
325 if (CharacterParams.CompareGroup(character.
Group, CharacterPrefab.HumanGroup))
332 if (targetCharacters.Any())
335 bool matchFound =
false;
336 foreach (Identifier target
in targetCharacters)
344 if (!matchFound) {
return false; }
349 public override XElement
Save(XElement parentElement)
351 Vector2 prevDetectOffset = detectOffset;
352 XElement element = base.Save(parentElement);
353 detectOffset = prevDetectOffset;
readonly CharacterParams Params
static readonly List< Character > CharacterList
bool MatchesSpeciesNameOrGroup(Identifier speciesNameOrGroup)
readonly AnimController AnimController
bool HasTag(Identifier tag)
float GetAttributeFloat(string key, float def)
bool GetAttributeBool(string key, bool def)
XAttribute? GetAttribute(string name)
virtual Vector2 WorldPosition
void SendSignal(string signal, string connectionName)
The base class for components holding the different functionalities of the item
void ApplyStatusEffects(ActionType type, float deltaTime, Character character=null, Limb targetLimb=null, Entity useTarget=null, Character user=null, Vector2? worldPosition=null, float afflictionMultiplier=1.0f)
Vector2 TransformedDetectOffset
MotionSensor(Item item, ContentXElement element)
override void Load(ContentXElement componentElement, bool usePrefabValues, IdRemap idRemap, bool isItemSwap)
override void Update(float deltaTime, Camera cam)
override XElement Save(XElement parentElement)
bool TriggersOn(Character character)
List< VoronoiCell > GetCells(Vector2 worldPos, int searchDepth=2)
override Vector2? WorldPosition
static List< Submarine > Loaded
Rectangle? Borders
Extents of the solid items/structures (ones with a physics body) and hulls
ActionType
ActionTypes define when a StatusEffect is executed.