2 using FarseerPhysics.Dynamics;
3 using FarseerPhysics.Dynamics.Contacts;
4 using Microsoft.Xna.Framework;
6 using System.Collections.Generic;
7 using System.Globalization;
15 public float Force {
get;
set; }
16 [
Editable,
Serialize(
false,
IsPropertySaveable.Yes, description:
"Determines if the force gets higher the closer the triggerer is to the center of the trigger.", alwaysUseInstanceValues:
true)]
20 [
Serialize(1.0f,
IsPropertySaveable.Yes, description:
"How much the fluctuation affects the force. 1 is the maximum fluctuation, 0 is no fluctuation.", alwaysUseInstanceValues:
true)]
21 private float ForceFluctuationStrength
25 return forceFluctuationStrength;
29 forceFluctuationStrength = Math.Clamp(value, 0.0f, 1.0f);
32 [Serialize(1.0f,
IsPropertySaveable.Yes, description:
"How fast (cycles per second) the force fluctuates.", alwaysUseInstanceValues:
true)]
33 private float ForceFluctuationFrequency
37 return forceFluctuationFrequency;
41 forceFluctuationFrequency = Math.Max(value, 0.01f);
44 [Serialize(0.01f,
IsPropertySaveable.Yes, description:
"How often (in seconds) the force fluctuation is calculated.", alwaysUseInstanceValues:
true)]
45 private float ForceFluctuationInterval
49 return forceFluctuationInterval;
53 forceFluctuationInterval = Math.Max(value, 0.01f);
58 private float Radius {
get;
set; }
59 private float RadiusInDisplayUnits {
get;
set; }
60 private bool TriggeredOnce {
get;
set; }
61 private float CurrentForceFluctuation {
get;
set; } = 1.0f;
63 private float ForceFluctuationTimer {
get;
set; }
64 private static float TimeInLevel
79 private readonly HashSet<Entity> triggerers =
new HashSet<Entity>();
80 private readonly
bool triggerOnce;
81 private readonly List<ISerializableEntity> statusEffectTargets =
new List<ISerializableEntity>();
85 private readonly List<StatusEffect> statusEffects =
new List<StatusEffect>();
89 private readonly List<Attack> attacks =
new List<Attack>();
91 private float forceFluctuationStrength;
92 private float forceFluctuationFrequency;
93 private float forceFluctuationInterval;
98 if (!Enum.TryParse(triggeredByAttribute, out triggeredBy))
100 DebugConsole.ThrowError($
"Error in ForceComponent config: \"{triggeredByAttribute}\" is not a valid triggerer type.",
104 string parentDebugName = $
"TriggerComponent in {item.Name}";
105 foreach (var subElement
in element.Elements())
107 switch (subElement.Name.ToString().ToLowerInvariant())
125 Radius = ConvertUnits.ToSimUnits(radiusAttribute *
item.
Scale);
144 private bool OnCollision(Fixture sender, Fixture other, Contact contact)
148 triggerers.Add(entity);
152 private void OnSeparation(Fixture sender, Fixture other, Contact contact)
154 if (LevelTrigger.GetEntity(other) is not Entity entity)
158 if (entity is Character character && (!character.Enabled || character.Removed) && triggerers.Contains(entity))
160 triggerers.
Remove(entity);
163 if (LevelTrigger.CheckContactsForOtherFixtures(
PhysicsBody, other, entity))
167 triggerers.Remove(entity);
185 if (TriggeredOnce) {
return; }
186 if (triggerers.Count > 0)
188 TriggeredOnce =
true;
198 ForceFluctuationTimer += deltaTime;
199 if (ForceFluctuationTimer >= ForceFluctuationInterval)
201 float v = MathF.Sin(2 * MathF.PI * ForceFluctuationFrequency * TimeInLevel);
202 float amount = MathUtils.InverseLerp(-1.0f, 1.0f, v);
203 CurrentForceFluctuation = MathHelper.Lerp(1.0f - ForceFluctuationStrength, 1.0f, amount);
204 ForceFluctuationTimer = 0.0f;
209 foreach (
Entity triggerer
in triggerers)
217 else if (triggerer is
Submarine submarine)
229 if (Math.Abs(
Force) < 0.01f)
236 if (c.AnimController.Collider.BodyType == BodyType.Dynamic)
238 if (c.AnimController.Collider.Enabled)
240 ApplyForce(c.AnimController.Collider);
242 foreach (var limb
in c.AnimController.Limbs)
244 ApplyForce(limb.body, multiplier: limb.Mass * c.AnimController.Collider.Mass / c.AnimController.Mass);
250 ApplyForce(s.SubBody.Body);
252 else if (triggerer is
Item i && i.
body !=
null)
261 private void ApplyForce(
PhysicsBody body,
float multiplier = 1.0f)
264 if (diff.LengthSquared() < 0.0001f) {
return; }
266 if (distanceFactor <= 0.0f) {
return; }
267 Vector2 force = distanceFactor * (CurrentForceFluctuation *
Force) * Vector2.Normalize(diff) * multiplier;
268 if (force.LengthSquared() < 0.01f) {
return; }
272 public override void Move(Vector2 amount,
bool ignoreContacts =
false)
299 base.ReceiveSignal(signal, connection);
300 switch (connection.
Name)
303 if (!FloatTryParse(signal, out
float force)) {
break; }
306 case "set_distancebasedforce":
307 if (!
bool.TryParse(signal.
value, out
bool distanceBasedForce)) {
break; }
310 case "set_forcefluctuation":
311 if (!
bool.TryParse(signal.
value, out
bool forceFluctuation)) {
break; }
314 case "set_forcefluctuationstrength":
315 if (!FloatTryParse(signal, out
float forceFluctuationStrength)) {
break; }
316 ForceFluctuationStrength = forceFluctuationStrength;
318 case "set_forcefluctuationfrequency":
319 if (!FloatTryParse(signal, out
float forceFluctuationFrequency)) {
break; }
320 ForceFluctuationFrequency = forceFluctuationFrequency;
322 case "set_forcefluctuationinterval":
323 if (!FloatTryParse(signal, out
float forceFluctuationInterval)) {
break; }
324 ForceFluctuationInterval = forceFluctuationInterval;
328 static bool FloatTryParse(
Signal signal, out
float value)
330 return float.TryParse(signal.
value, NumberStyles.Any, CultureInfo.InvariantCulture, out value);
static readonly List< Character > CharacterList
string? GetAttributeString(string key, string? def)
float GetAttributeFloat(string key, float def)
ContentPackage? ContentPackage
bool GetAttributeBool(string key, bool def)
static GameSession?? GameSession
static NetworkMember NetworkMember
override Vector2? SimPosition
void SetTransform(Vector2 simPosition, float rotation, bool findNewHull=true, bool setPrevTransform=true)
void SendSignal(string signal, string connectionName)
The base class for components holding the different functionalities of the item
readonly ContentXElement originalElement
override void ReceiveSignal(Signal signal, Connection connection)
override void RemoveComponentSpecific()
override void OnItemLoaded()
Called when all the components of the item have been loaded. Use to initialize connections between co...
override void OnMapLoaded()
Called when all items have been loaded. Use to initialize connections between items.
override void Move(Vector2 amount, bool ignoreContacts=false)
bool ApplyEffectsToCharactersInsideSub
override void Update(float deltaTime, Camera cam)
TriggerComponent(Item item, ContentXElement element)
static void LoadStatusEffect(List< StatusEffect > statusEffects, ContentXElement element, string parentDebugName)
static void ApplyStatusEffects(List< StatusEffect > statusEffects, Vector2 worldPosition, Entity triggerer, float deltaTime, List< ISerializableEntity > targets)
static Entity GetEntity(Fixture fixture)
static void LoadAttack(ContentXElement element, string parentDebugName, bool triggerOnce, List< Attack > attacks)
static bool IsTriggeredByEntity(Entity entity, TriggererType triggeredBy, bool mustBeOutside=false,(bool mustBe, Submarine sub) mustBeOnSpecificSub=default)
static void RemoveInActiveTriggerers(PhysicsBody physicsBody, HashSet< Entity > triggerers)
static float GetDistanceFactor(PhysicsBody triggererBody, PhysicsBody triggerBody, float colliderRadius)
static void ApplyAttacks(List< Attack > attacks, IDamageable damageable, Vector2 worldPosition, float deltaTime)
Applies attacks to a damageable.
static Category GetCollisionCategories(TriggererType triggeredBy)
void ApplyForce(Vector2 force, float maxVelocity=NetConfig.MaxPhysicsBodyVelocity)
bool SetTransform(Vector2 simPosition, float rotation, bool setPrevTransform=true)
bool SetTransformIgnoreContacts(Vector2 simPosition, float rotation, bool setPrevTransform=true)