2 using FarseerPhysics.Dynamics;
3 using Microsoft.Xna.Framework;
5 using System.Collections.Generic;
19 private readonly HashSet<Identifier> fixableEntities;
20 private readonly HashSet<Identifier> nonFixableEntities;
21 private Vector2 pickedPosition;
22 private float activeTimer;
24 private Vector2 debugRayStartPos, debugRayEndPos;
26 private readonly List<Body> ignoredBodies =
new List<Body>();
35 public float Range {
get;
set; }
37 [
Serialize(0.0f,
IsPropertySaveable.No, description:
"Random spread applied to the firing angle when used by a character with sufficient skills to use the tool (in degrees).")]
44 [
Serialize(0.0f,
IsPropertySaveable.No, description:
"Random spread applied to the firing angle when used by a character with insufficient skills to use the tool (in degrees).")]
63 [
Serialize(0.0f,
IsPropertySaveable.No, description:
"How many units of damage the item removes from destructible level walls per second.")]
78 [
Serialize(
"0.0,0.0",
IsPropertySaveable.No, description:
"The position of the barrel as an offset from the item's center (in pixels).")]
84 [
Serialize(
false,
IsPropertySaveable.No, description:
"Can the item repair multiple things at once, or will it only affect the first thing the ray from the barrel hits.")]
87 [
Serialize(
true,
IsPropertySaveable.No, description:
"Can the item repair multiple walls at once? Only relevant if RepairMultiple is true.")]
93 [
Serialize(100.0f,
IsPropertySaveable.No, description:
"How far two walls need to not be considered overlapping and to stop the ray.")]
96 [
Serialize(1.0f,
IsPropertySaveable.No, description:
"How fast the tool detaches level resources (e.g. minerals). Acts as a multiplier on the speed: with a value of 2, detaching an item whose DeattachDuration is set to 30 seconds would take 15 seconds.")]
108 [
Serialize(0.0f,
IsPropertySaveable.No, description:
"The probability of starting a fire somewhere along the ray fired from the barrel (for example, 0.1 = 10% chance to start a fire during a second of use).")]
114 [
Serialize(0.0f,
IsPropertySaveable.No, description:
"Rotation of the barrel in degrees."),
Editable(MinValueFloat = 0, MaxValueFloat = 360, VectorComponentLabels =
new string[] {
"editable.minvalue",
"editable.maxvalue" })]
127 if (
item.
body.
Dir < 0.0f) { flippedPos.X = -flippedPos.X; }
128 return Vector2.Transform(flippedPos, bodyTransform);
133 : base(
item, element)
139 DebugConsole.ThrowError(
"Error in item \"" +
item.
Name +
"\" - RepairTool damage should be configured using a StatusEffect with Afflictions, not the limbfixamount attribute.",
143 fixableEntities =
new HashSet<Identifier>();
144 nonFixableEntities =
new HashSet<Identifier>();
145 foreach (var subElement
in element.Elements())
147 switch (subElement.Name.ToString().ToLowerInvariant())
150 if (subElement.GetAttribute(
"name") !=
null)
152 DebugConsole.ThrowError(
"Error in RepairTool " +
item.
Name +
" - use identifiers instead of names to configure fixable entities.",
153 contentPackage: element.ContentPackage);
154 fixableEntities.Add(subElement.GetAttribute(
"name").Value.ToIdentifier());
158 foreach (Identifier
id in subElement.GetAttributeIdentifierArray(
"identifier", Array.Empty<Identifier>()))
160 fixableEntities.Add(
id);
165 foreach (Identifier
id in subElement.GetAttributeIdentifierArray(
"identifier", Array.Empty<Identifier>()))
167 nonFixableEntities.Add(
id);
174 InitProjSpecific(element);
181 activeTimer -= deltaTime;
182 if (activeTimer <= 0.0f) {
IsActive =
false; }
187 if (character !=
null)
192 float degreeOfSuccess = character ==
null ? 0.5f :
DegreeOfSuccess(character);
195 if (Rand.Range(0.0f, 0.5f) > degreeOfSuccess)
229 Vector2 rayStartWorld;
230 Vector2 sourcePos = character?.AnimController ==
null ?
item.
SimPosition : character.AnimController.AimSourceSimPos;
233 if (
Submarine.
PickBody(sourcePos, barrelPos, collisionCategory: Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking) ==
null)
251 if (MathUtils.GetLineRectangleIntersection(ConvertUnits.ToDisplayUnits(sourcePos), ConvertUnits.ToDisplayUnits(rayStart),
item.
CurrentHull.
Rect, out Vector2 hullIntersection))
255 Vector2 rayDir = rayStart.NearlyEquals(sourcePos) ? Vector2.Zero : Vector2.Normalize(rayStart - sourcePos);
256 rayStartWorld = ConvertUnits.ToSimUnits(hullIntersection - rayDir * 5.0f);
265 float angle = MathHelper.ToRadians(
BarrelRotation) + spread * Rand.Range(-0.5f, 0.5f);
272 Vector2 rayEnd = rayStartWorld + ConvertUnits.ToSimUnits(
new Vector2((
float)Math.Cos(angle), (
float)Math.Sin(angle)) *
Range * dir);
274 ignoredBodies.Clear();
275 if (character !=
null)
277 foreach (
Limb limb
in character.AnimController.Limbs)
279 if (Rand.Range(0.0f, 0.5f) > degreeOfSuccess)
continue;
282 ignoredBodies.Add(character.AnimController.Collider.FarseerBody);
288 debugRayStartPos = ConvertUnits.ToDisplayUnits(rayStartWorld);
289 debugRayEndPos = ConvertUnits.ToDisplayUnits(rayEnd);
292 if (parentSub ==
null)
296 Rectangle subBorders = sub.
Borders;
302 Repair(rayStartWorld - sub.
SimPosition, rayEnd - sub.
SimPosition, deltaTime, character, degreeOfSuccess, ignoredBodies);
304 Repair(rayStartWorld, rayEnd, deltaTime, character, degreeOfSuccess, ignoredBodies);
308 Repair(rayStartWorld - parentSub.
SimPosition, rayEnd - parentSub.
SimPosition, deltaTime, character, degreeOfSuccess, ignoredBodies);
311 UseProjSpecific(deltaTime, rayStartWorld);
316 partial
void UseProjSpecific(
float deltaTime, Vector2 raystart);
318 private static readonly List<Body> hitBodies =
new List<Body>();
319 private readonly HashSet<Character> hitCharacters =
new HashSet<Character>();
320 private readonly List<FireSource> fireSourcesInRange =
new List<FireSource>();
321 private void Repair(Vector2 rayStart, Vector2 rayEnd,
float deltaTime,
Character user,
float degreeOfSuccess, List<Body> ignoredBodies)
323 var collisionCategories = Physics.CollisionWall | Physics.CollisionItem | Physics.CollisionLevel | Physics.CollisionRepairableWall;
326 collisionCategories |= Physics.CollisionCharacter;
332 static bool CanSeverJoints(
ActionType type, Dictionary<
ActionType, List<StatusEffect>> effectList) =>
333 effectList.TryGetValue(type, out List<StatusEffect> effects) && effects.Any(e => e.SeverLimbsProbability > 0);
337 float rangeSqr = ConvertUnits.ToSimUnits(
Range);
338 rangeSqr *= rangeSqr;
347 if (Vector2.DistanceSquared(limb.SimPosition,
item.
SimPosition) < rangeSqr && Vector2.Dot(rayEnd - rayStart, limb.SimPosition - rayStart) > 0)
357 float lastPickedFraction = 0.0f;
360 var bodies =
Submarine.PickBodies(rayStart, rayEnd, ignoredBodies, collisionCategories,
361 ignoreSensors:
false,
362 customPredicate: (Fixture f) =>
366 if (RepairThroughHoles && f.Body?.UserData is Structure) { return false; }
367 if (f.Body?.UserData is PhysicsBody) { return false; }
369 if (f.Body?.UserData is
Item it && it.GetComponent<Planter>() !=
null) { return false; }
370 if (f.Body?.UserData as
string ==
"ruinroom") { return false; }
371 if (f.Body?.UserData is VineTile && !(
FireDamage > 0)) { return false; }
374 allowInsideFixture:
true);
377 hitBodies.AddRange(bodies.Distinct());
379 lastPickedFraction =
Submarine.LastPickedFraction;
380 Type lastHitType =
null;
381 hitCharacters.Clear();
382 foreach (Body body
in hitBodies)
384 Type bodyType = body.UserData?.GetType();
388 if (lastHitType == typeof(
Item) || lastHitType == typeof(Structure)) {
break; }
390 if (!
RepairMultipleWalls && (bodyType == typeof(Structure) || (body.UserData as
Item)?.GetComponent<Door>() !=
null)) {
break; }
393 if (body.UserData is Limb limb)
395 hitCharacter = limb.character;
397 else if (body.UserData is Character character)
399 hitCharacter = character;
402 if (hitCharacter !=
null)
404 if (hitCharacters.Contains(hitCharacter)) {
continue; }
405 hitCharacters.Add(hitCharacter);
410 float thisBodyFraction =
Submarine.LastPickedBodyDist(body);
415 pickedPosition = rayStart + (rayEnd - rayStart) * thisBodyFraction;
416 if (FixBody(user, pickedPosition, deltaTime, degreeOfSuccess, body))
418 lastPickedFraction = thisBodyFraction;
419 if (bodyType !=
null) { lastHitType = bodyType; }
425 var pickedBody =
Submarine.PickBody(rayStart, rayEnd,
426 ignoredBodies, collisionCategories,
427 ignoreSensors:
false,
428 customPredicate: (Fixture f) =>
433 if (f.Body?.UserData is PhysicsBody) {
return false; }
435 if (f.Body?.UserData as
string ==
"ruinroom") {
return false; }
436 if (f.Body?.UserData is VineTile && !(
FireDamage > 0)) {
return false; }
438 if (f.Body?.UserData is
Item targetItem)
443 if (targetItem.GetComponent<Door>() ==
null && targetItem.Condition <= 0) {
return false; }
447 if (targetItem.Condition <= 0) {
return false; }
450 return f.Body?.UserData !=
null;
452 allowInsideFixture:
true);
453 pickedPosition =
Submarine.LastPickedPosition;
454 FixBody(user, pickedPosition, deltaTime, degreeOfSuccess, pickedBody);
455 lastPickedFraction =
Submarine.LastPickedFraction;
460 fireSourcesInRange.Clear();
462 for (
float x = 0.0f; x <= lastPickedFraction; x += 0.1f)
464 Vector2 displayPos = ConvertUnits.ToDisplayUnits(rayStart + (rayEnd - rayStart) * x);
468 if (hull ==
null)
continue;
469 foreach (FireSource fs
in hull.FireSources)
471 if (fs.IsInDamageRange(displayPos, 100.0f) && !fireSourcesInRange.Contains(fs))
473 fireSourcesInRange.Add(fs);
476 foreach (FireSource fs
in hull.FakeFireSources)
478 if (fs.IsInDamageRange(displayPos, 100.0f) && !fireSourcesInRange.Contains(fs))
480 fireSourcesInRange.Add(fs);
485 foreach (FireSource fs
in fireSourcesInRange)
489 if (!(fs is DummyFireSource))
491 GameMain.Server.KarmaManager.OnExtinguishingFire(user, deltaTime);
504 if (it.Submarine ==
item.
Submarine && it.GetComponent<Planter>() is { } planter)
506 if (it.GetComponent<Holdable>() is { } holdable && holdable.Attachable && !holdable.Attached) {
continue; }
508 Rectangle collisionRect = it.WorldRect;
509 collisionRect.Y -= collisionRect.Height;
510 if (collisionRect.Left < pos.X && collisionRect.Right > pos.X && collisionRect.Bottom < pos.Y)
512 Body collision =
Submarine.PickBody(rayStart, it.SimPosition, ignoredBodies, collisionCategories);
513 if (collision ==
null)
515 for (var i = 0; i < planter.GrowableSeeds.Length; i++)
517 Growable seed = planter.GrowableSeeds[i];
518 if (seed ==
null || seed.Decayed) {
continue; }
523 float barOffset = 10f * GUI.Scale;
524 Vector2 offset = planter.PlantSlots.ContainsKey(i) ? planter.PlantSlots[i].Offset : Vector2.Zero;
525 user?.UpdateHUDProgressBar(planter, planter.Item.DrawPosition +
new Vector2(barOffset, 0) + offset, seed.Health / seed.MaxHealth, GUIStyle.Blue, GUIStyle.Blue,
"progressbar.watering");
534 if (GameMain.NetworkMember ==
null || GameMain.NetworkMember.IsServer)
538 Vector2 displayPos = ConvertUnits.ToDisplayUnits(rayStart + (rayEnd - rayStart) * lastPickedFraction * 0.9f);
540 new FireSource(displayPos, sourceCharacter: user);
545 private bool FixBody(Character user, Vector2 hitPosition,
float deltaTime,
float degreeOfSuccess, Body targetBody)
547 if (targetBody?.UserData ==
null) {
return false; }
549 if (targetBody.UserData is Structure targetStructure)
551 if (targetStructure.IsPlatform) {
return false; }
552 int sectionIndex = targetStructure.FindSectionIndex(ConvertUnits.ToDisplayUnits(pickedPosition));
553 if (sectionIndex < 0) {
return false; }
555 if (!fixableEntities.Contains(
"structure") && !fixableEntities.Contains(targetStructure.Prefab.Identifier)) {
return true; }
556 if (nonFixableEntities.Contains(targetStructure.Prefab.Identifier) || nonFixableEntities.Any(t => targetStructure.Tags.Contains(t))) {
return false; }
558 ApplyStatusEffectsOnTarget(user, deltaTime,
ActionType.OnUse, structure: targetStructure);
559 ApplyStatusEffectsOnTarget(user, deltaTime,
ActionType.OnSuccess, structure: targetStructure);
560 FixStructureProjSpecific(user, deltaTime, targetStructure, sectionIndex);
562 float structureFixAmount = StructureFixAmount;
563 if (structureFixAmount >= 0f)
565 structureFixAmount *= 1 + user.GetStatValue(
StatTypes.RepairToolStructureRepairMultiplier);
566 structureFixAmount *= 1 + item.GetQualityModifier(Quality.StatType.RepairToolStructureRepairMultiplier);
570 structureFixAmount *= 1 + user.GetStatValue(
StatTypes.RepairToolStructureDamageMultiplier);
571 structureFixAmount *= 1 + item.GetQualityModifier(Quality.StatType.RepairToolStructureDamageMultiplier);
574 var didLeak = targetStructure.SectionIsLeakingFromOutside(sectionIndex);
576 targetStructure.AddDamage(sectionIndex, -structureFixAmount * degreeOfSuccess, user);
578 if (didLeak && !targetStructure.SectionIsLeakingFromOutside(sectionIndex))
585 for (
int i = -1; i < 2; i += 2)
587 int nextSectionLength = targetStructure.SectionLength(sectionIndex + i);
588 if ((sectionIndex == 1 && i == -1) ||
589 (sectionIndex == targetStructure.SectionCount - 2 && i == 1) ||
590 (nextSectionLength > 0 && nextSectionLength <
Structure.WallSectionSize * 0.3f))
593 targetStructure.AddDamage(sectionIndex + i, -structureFixAmount * degreeOfSuccess);
600 if (Level.Loaded?.ExtraWalls.Find(w => w.Body == cell.Body) is DestructibleLevelWall levelWall)
602 levelWall.AddDamage(-LevelWallFixAmount * deltaTime, ConvertUnits.ToDisplayUnits(hitPosition));
606 else if (targetBody.UserData is LevelObject levelObject && levelObject.Prefab.TakeLevelWallDamage)
608 levelObject.AddDamage(-LevelWallFixAmount, deltaTime, item);
611 else if (targetBody.UserData is Character targetCharacter)
613 if (targetCharacter.Removed) {
return false; }
614 targetCharacter.LastDamageSource = item;
615 Limb closestLimb =
null;
616 float closestDist =
float.MaxValue;
617 foreach (Limb limb
in targetCharacter.AnimController.Limbs)
619 if (limb.Removed || limb.IgnoreCollisions || limb.Hidden || limb.IsSevered) {
continue; }
620 float dist = Vector2.DistanceSquared(item.SimPosition, limb.SimPosition);
621 if (dist < closestDist)
628 if (closestLimb !=
null && !MathUtils.NearlyEqual(TargetForce, 0.0f))
630 Vector2 dir = closestLimb.WorldPosition - item.WorldPosition;
631 dir = dir.LengthSquared() < 0.0001f ? Vector2.UnitY : Vector2.Normalize(dir);
632 closestLimb.body.ApplyForce(dir * TargetForce, maxVelocity: 10.0f);
635 ApplyStatusEffectsOnTarget(user, deltaTime,
ActionType.OnUse, character: targetCharacter, limb: closestLimb);
636 ApplyStatusEffectsOnTarget(user, deltaTime,
ActionType.OnSuccess, character: targetCharacter, limb: closestLimb);
637 FixCharacterProjSpecific(user, deltaTime, targetCharacter);
640 else if (targetBody.UserData is Limb targetLimb)
642 if (targetLimb.character ==
null || targetLimb.character.Removed) {
return false; }
644 if (!MathUtils.NearlyEqual(TargetForce, 0.0f))
646 Vector2 dir = targetLimb.WorldPosition - item.WorldPosition;
647 dir = dir.LengthSquared() < 0.0001f ? Vector2.UnitY : Vector2.Normalize(dir);
648 targetLimb.body.ApplyForce(dir * TargetForce, maxVelocity: 10.0f);
651 targetLimb.character.LastDamageSource = item;
652 ApplyStatusEffectsOnTarget(user, deltaTime,
ActionType.OnUse, character: targetLimb.character, limb: targetLimb);
653 ApplyStatusEffectsOnTarget(user, deltaTime,
ActionType.OnSuccess, character: targetLimb.character, limb: targetLimb);
654 FixCharacterProjSpecific(user, deltaTime, targetLimb.character);
657 else if (targetBody.UserData is
Item targetItem)
659 if (!HitItems || !targetItem.IsInteractable(user)) {
return false; }
661 var levelResource = targetItem.GetComponent<LevelResource>();
662 if (levelResource !=
null && levelResource.Attached &&
663 levelResource.RequiredItems.Any() &&
664 levelResource.HasRequiredItems(user, addMessage:
false))
666 float addedDetachTime = deltaTime *
668 (1f + user.GetStatValue(
StatTypes.RepairToolDeattachTimeMultiplier)) *
669 (1f + item.GetQualityModifier(Quality.StatType.RepairToolDeattachTimeMultiplier));
670 levelResource.DeattachTimer += addedDetachTime;
672 if (targetItem.Prefab.ShowHealthBar &&
Character.Controlled !=
null &&
675 Character.Controlled.UpdateHUDProgressBar(
677 targetItem.WorldPosition,
678 levelResource.DeattachTimer / levelResource.DeattachDuration,
679 GUIStyle.Red, GUIStyle.Green,
"progressbar.deattaching");
682 FixItemProjSpecific(user, deltaTime, targetItem, showProgressBar:
false);
686 if (!targetItem.Prefab.DamagedByRepairTools) {
return false; }
690 if (targetItem.GetComponent<Door>() ==
null && targetItem.Condition <= 0) {
return false; }
694 if (targetItem.Condition <= 0) {
return false; }
697 targetItem.IsHighlighted =
true;
699 ApplyStatusEffectsOnTarget(user, deltaTime,
ActionType.OnUse, targetItem);
700 ApplyStatusEffectsOnTarget(user, deltaTime,
ActionType.OnSuccess, targetItem);
702 if (targetItem.body !=
null && !MathUtils.NearlyEqual(TargetForce, 0.0f))
704 Vector2 dir = targetItem.WorldPosition - item.WorldPosition;
705 dir = dir.LengthSquared() < 0.0001f ? Vector2.UnitY : Vector2.Normalize(dir);
706 targetItem.body.ApplyForce(dir * TargetForce, maxVelocity: 10.0f);
709 FixItemProjSpecific(user, deltaTime, targetItem, showProgressBar:
true);
714 if (branch.ParentBallastFlora is { } ballastFlora)
716 ballastFlora.DamageBranch(branch, FireDamage * deltaTime, BallastFloraBehavior.AttackType.Fire, user);
722 partial
void FixStructureProjSpecific(Character user,
float deltaTime, Structure targetStructure,
int sectionIndex);
723 partial
void FixCharacterProjSpecific(Character user,
float deltaTime, Character targetCharacter);
724 partial
void FixItemProjSpecific(Character user,
float deltaTime,
Item targetItem,
bool showProgressBar);
726 private float sinTime;
727 private float repairTimer;
728 private Gap previousGap;
729 private readonly
float repairTimeOut = 5;
737 if (leak.Submarine ==
null || leak.Submarine != character.
Submarine)
742 if (leak != previousGap)
748 float dist = fromCharacterToLeak.Length();
750 if (dist > reach * 2)
759 pathSteering.ResetPath();
765 Math.Abs(fromCharacterToLeak.X) < 100.0f && fromCharacterToLeak.Y < 0.0f && fromCharacterToLeak.Y > -150.0f)
772 if (dist > reach * 0.8f || dist > reach * 0.5f && character.
AnimController.
Limbs.Any(l => l.InWater))
775 Vector2 dir = Vector2.Normalize(fromCharacterToLeak);
782 else if (dist < reach * 0.25f && !character.
IsClimbing)
801 Vector2 mousePos = ConvertUnits.ToSimUnits(character.
CursorPosition);
803 float newRotation = MathUtils.VectorToAngle(diff);
806 if (VectorExtensions.Angle(VectorExtensions.Forward(torso.body.TransformedRotation), fromCharacterToLeak) < MathHelper.PiOver4)
809 Vector2 moveDir = leak.IsHorizontal ? Vector2.UnitY : Vector2.UnitX;
814 if (item.RequireAimToUse)
817 sinTime += deltaTime * 5;
820 Vector2 fromItemToLeak = leak.WorldPosition - item.WorldPosition;
821 var angle = VectorExtensions.Angle(VectorExtensions.Forward(item.body.TransformedRotation), fromItemToLeak);
823 if (angle < MathHelper.PiOver4)
825 if (
Submarine.
PickBody(item.SimPosition, leak.SimPosition, collisionCategory: Physics.CollisionWall, allowInsideFixture:
true)?.UserData is
Item i)
844 if (
Submarine.
PickBodies(item.SimPosition, leak.SimPosition, collisionCategory: Physics.CollisionCharacter).None(hit =>
848 if (c == character) { return false; }
855 Use(deltaTime, character);
858 repairTimer += deltaTime;
859 if (repairTimer > repairTimeOut)
862 DebugConsole.NewMessage($
"{character.Name}: timed out while welding a leak in {leak.FlowTargetHull.DisplayName}.", color: Color.Yellow);
875 bool leakFixed = (leak.Open <= 0.0f || leak.Removed) &&
876 (leak.ConnectedWall ==
null || leak.ConnectedWall.Sections.Max(s => s.damage) < 0.1f);
878 if (leakFixed && leak.FlowTargetHull?.DisplayName !=
null && character.IsOnPlayerTeam)
880 if (!leak.FlowTargetHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.Open > 0.0f))
882 character.Speak(TextManager.GetWithVariable(
"DialogLeaksFixed",
"[roomname]", leak.FlowTargetHull.DisplayName,
FormatCapitals.Yes).Value,
null, 0.0f,
"leaksfixed".ToIdentifier(), 10.0f);
886 character.Speak(TextManager.GetWithVariable(
"DialogLeakFixed",
"[roomname]", leak.FlowTargetHull.DisplayName,
FormatCapitals.Yes).Value,
null, 0.0f,
"leakfixed".ToIdentifier(), 10.0f);
899 private static List<ISerializableEntity> currentTargets =
new List<ISerializableEntity>();
900 private void ApplyStatusEffectsOnTarget(Character user,
float deltaTime, ActionType actionType,
Item targetItem =
null, Character character =
null, Limb limb =
null, Structure structure =
null)
902 if (statusEffectLists ==
null) {
return; }
903 if (!statusEffectLists.TryGetValue(actionType, out List<StatusEffect> statusEffects)) {
return; }
905 foreach (StatusEffect effect
in statusEffects)
907 currentTargets.Clear();
908 effect.SetUser(user);
909 if (effect.HasTargetType(StatusEffect.TargetType.UseTarget))
911 if (targetItem !=
null)
913 currentTargets.AddRange(targetItem.AllPropertyObjects);
915 if (structure !=
null)
917 currentTargets.Add(structure);
919 if (character !=
null)
921 currentTargets.Add(character);
923 effect.Apply(actionType, deltaTime, item, currentTargets);
925 else if (effect.HasTargetType(StatusEffect.TargetType.Character))
927 currentTargets.Add(user);
928 effect.Apply(actionType, deltaTime, item, currentTargets);
930 else if (effect.HasTargetType(StatusEffect.TargetType.Limb))
932 currentTargets.Add(limb);
933 effect.Apply(actionType, deltaTime, item, currentTargets);
937 if (user ==
null) {
return; }
940 foreach (ISerializableEntity target
in currentTargets)
942 if (target is not Door door) {
continue; }
943 if (!door.CanBeWelded || !door.Item.IsInteractable(user)) {
continue; }
944 foreach (var propertyEffect
in effect.PropertyEffects)
946 if (propertyEffect.propertyName !=
"stuck") {
continue; }
947 if (door.SerializableProperties ==
null || !door.SerializableProperties.TryGetValue(propertyEffect.propertyName, out SerializableProperty property)) {
continue; }
948 object value =
property.GetValue(target);
951 bool isCutting = propertyEffect.value is
float and < 0;
952 var progressBar = user.UpdateHUDProgressBar(door, door.Item.WorldPosition, door.Stuck / 100, Color.DarkGray * 0.5f, Color.White,
953 textTag: isCutting ?
"progressbar.cutting" :
"progressbar.welding");
954 if (progressBar !=
null) { progressBar.Size =
new Vector2(60.0f, 20.0f); }
955 if (!isCutting) { HintManager.OnWeldingDoor(user, door); }
SteeringManager SteeringManager
static float CalculateReach(RepairTool repairTool, Character character)
Vector2 AimSourceWorldPos
void SetInput(InputType inputType, bool hit, bool held)
virtual AIController AIController
override Vector2? SimPosition
static readonly List< Character > CharacterList
readonly AnimController AnimController
ContentPackage? ContentPackage
XAttribute? GetAttribute(string name)
virtual Vector2 WorldPosition
readonly List< Gap > ConnectedGaps
static Hull FindHull(Vector2 position, Hull guess=null, bool useWorldCoordinates=true, bool inclusive=true)
Returns the hull which contains the point (or null if it isn't inside any)
static bool IsFriendly(Character me, Character other, bool onlySameTeam=false)
override Vector2? SimPosition
bool IsShootable
Should the item's Use method be called with the "Use" or with the "Shoot" key?
override Vector2? Position
bool RequireAimToUse
If true, the user has to hold the "aim" key before use is registered. False by default.
override string Name
Note that this is not a LocalizedString instance, just the current name of the item as a string....
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)
readonly Dictionary< ActionType, List< StatusEffect > > statusEffectLists
float DegreeOfSuccess(Character character)
Returns 0.0f-1.0f based on how well the Character can use the itemcomponent
float TransformedRotation
Takes flipping (Dir) into account.
void SmoothRotate(float targetRotation, float force=10.0f, bool wrapAngle=true)
Rotate the body towards the target rotation in the "shortest direction", taking into account the curr...
Limb GetLimb(LimbType limbType, bool excludeSevered=true, bool excludeLimbsWithSecondaryType=false, bool useSecondaryType=false)
Note that if there are multiple limbs of the same type, only the first (valid) limb is returned.
void SteeringManual(float deltaTime, Vector2 velocity)
static Vector2 LastPickedNormal
Submarine(SubmarineInfo info, bool showErrorMessages=true, Func< Submarine, List< MapEntity >> loadEntities=null, IdRemap linkedRemap=null)
override Vector2? WorldPosition
static bool RectContains(Rectangle rect, Vector2 pos, bool inclusive=false)
static List< Submarine > Loaded
static IEnumerable< Body > PickBodies(Vector2 rayStart, Vector2 rayEnd, IEnumerable< Body > ignoredBodies=null, Category? collisionCategory=null, bool ignoreSensors=true, Predicate< Fixture > customPredicate=null, bool allowInsideFixture=false)
Returns a list of physics bodies the ray intersects with, sorted according to distance (the closest b...
override Vector2 SimPosition
Rectangle? Borders
Extents of the solid items/structures (ones with a physics body) and hulls
override Vector2? Position
static Body PickBody(Vector2 rayStart, Vector2 rayEnd, IEnumerable< Body > ignoredBodies=null, Category? collisionCategory=null, bool ignoreSensors=true, Predicate< Fixture > customPredicate=null, bool allowInsideFixture=false)
static Vector2 LastPickedPosition
ActionType
ActionTypes define when a StatusEffect is executed.
StatTypes
StatTypes are used to alter several traits of a character. They are mostly used by talents.
@ Character
Characters only
@ Structure
Structures and hulls, but also items (for backwards support)!