6 using FarseerPhysics.Dynamics;
7 using Microsoft.Xna.Framework;
8 using Microsoft.Xna.Framework.Graphics;
10 using System.Collections.Generic;
17 public HashSet<SpriteDeformation>
SpriteDeformations {
get;
protected set; } =
new HashSet<SpriteDeformation>();
24 partial
void UpdateNetPlayerPositionProjSpecific(
float deltaTime,
float lowestSubPos)
104 newVelocity = newVelocity.ClampLength(100.0f);
105 if (!MathUtils.IsValid(newVelocity)) { newVelocity = Vector2.Zero; }
113 if (distSqrd > errorTolerance)
118 if (distSqrd > 10.0f)
128 SetPosition(newPosition, lerp: distSqrd < 5.0f, ignorePlatforms:
false);
143 float mainLimbErrorTolerance = 0.1f;
199 if (localPosIndex > -1)
227 if (localPos.SelectedSecondaryItem != serverPos.SelectedSecondaryItem)
229 if (serverPos.SelectedSecondaryItem ==
null || serverPos.SelectedSecondaryItem.
Removed)
235 serverPos.SelectedSecondaryItem.
TryInteract(
character, ignoreRequiredItems:
true, forceSelectKey:
true);
255 if (serverHull !=
null && clientHull !=
null && serverHull.
Submarine != clientHull.
Submarine)
266 float rotationError = serverPos.
Rotation.HasValue && localPos.
Rotation.HasValue ?
273 if (pointHull != clientHull && ((pointHull ==
null) || (clientHull ==
null) || (pointHull.
Submarine == clientHull.
Submarine)))
break;
277 float errorMagnitude = positionError.Length();
278 if (errorMagnitude > 0.5f)
283 else if (errorMagnitude > 0.01f)
298 partial
void ImpactProjSpecific(
float impact, Body body)
300 float volume = MathHelper.Clamp(impact - 3.0f, 0.5f, 1.0f);
315 GameMain.GameScreen.Cam.Shake = Math.Min(Math.Max(
strongestImpact, GameMain.GameScreen.Cam.Shake), 3.0f);
321 limb.LastImpactSoundTime = (float)Timing.TotalTime;
335 if (limb.type == wearable.
Limb && !
string.IsNullOrWhiteSpace(wearable.
Sound))
342 partial
void Splash(
Limb limb,
Hull limbHull)
345 for (
int i = 0; i < MathHelper.Clamp(Math.Abs(limb.
LinearVelocity.Y), 1.0f, 5.0f); i++)
350 Rand.Range(0.0f, MathHelper.TwoPi), limbHull);
358 GameMain.ParticleManager.CreateParticle(
"bubbles",
366 if (splashSoundTimer <= 0.0f)
369 splashSoundTimer = 0.5f;
373 GameMain.ParticleManager.CreateParticle(
"bubbles",
380 partial
void SetupDrawOrder()
384 float startDepth = 0.1f;
385 float increment = 0.001f;
388 if (otherCharacter ==
character) {
continue; }
389 startDepth += increment;
392 List<Limb> depthSortedLimbs =
Limbs.OrderBy(l => l.DefaultSpriteDepth).ToList();
393 foreach (Limb limb
in Limbs)
396 if (sprite ==
null) {
continue; }
397 sprite.
Depth = startDepth + depthSortedLimbs.IndexOf(limb) * 0.00001f;
398 foreach (var conditionalSprite
in limb.ConditionalSprites)
400 if (conditionalSprite.Exclusive)
402 conditionalSprite.ActiveSprite.Depth = sprite.Depth;
406 foreach (Limb limb
in Limbs)
408 if (limb.ActiveSprite ==
null) {
continue; }
409 if (limb.Params.InheritLimbDepth ==
LimbType.None) {
continue; }
410 var matchingLimb =
GetLimb(limb.Params.InheritLimbDepth);
411 if (matchingLimb !=
null)
413 limb.ActiveSprite.Depth = matchingLimb.ActiveSprite.Depth - 0.0000001f;
417 depthSortedLimbs.Reverse();
421 partial
void UpdateProjSpecific(
float deltaTime, Camera cam)
425 LimbJoints.ForEach(j => j.UpdateDeformations(deltaTime));
428 if (
character.
IsDead && deformation.Params.StopWhenHostIsDead) {
continue; }
430 if (deformation.Params.UseMovementSine)
432 if (
this is AnimController animator)
434 deformation.Phase = MathUtils.WrapAngleTwoPi(animator.WalkPos * deformation.Params.Frequency + MathHelper.Pi * deformation.Params.SineOffset);
439 deformation.Update(deltaTime);
444 partial
void FlipProjSpecific()
446 foreach (Limb limb
in Limbs)
448 if (limb ==
null || limb.IsSevered || !limb.DoesMirror) {
continue; }
450 FlipSprite(limb.DeformSprite?.Sprite ?? limb.Sprite);
451 foreach (var conditionalSprite
in limb.ConditionalSprites)
453 FlipSprite(conditionalSprite.DeformableSprite?.Sprite ?? conditionalSprite.Sprite);
456 static void FlipSprite(Sprite sprite)
458 if (sprite ==
null) {
return; }
459 Vector2 spriteOrigin = sprite.Origin;
460 spriteOrigin.X = sprite.SourceRect.Width - spriteOrigin.X;
461 sprite.Origin = spriteOrigin;
465 partial
void SeverLimbJointProjSpecific(LimbJoint limbJoint,
bool playSound)
467 foreach (Limb limb
in new Limb[] { limbJoint.LimbA, limbJoint.LimbB })
472 if (emitter?.Prefab ==
null) {
continue; }
482 var damageSound =
character.
GetSound(s => s.Type == CharacterSound.SoundType.Damage);
484 if (!limbJoint.Params.BreakSound.IsNullOrEmpty() && !limbJoint.Params.BreakSound.Equals(
"none", StringComparison.OrdinalIgnoreCase))
486 SoundPlayer.PlayDamageSound(limbJoint.Params.BreakSound, 1.0f, limbJoint.LimbA.body.DrawPosition, range: range);
493 if (simplePhysicsEnabled) {
return; }
499 DebugConsole.ThrowError(
"Failed to draw a ragdoll, limbs have been removed. Character: \"" +
character.
Name +
"\", removed: " +
character.
Removed +
"\n" + Environment.StackTrace.CleanupStackTrace());
500 GameAnalyticsManager.AddErrorEventOnce(
"Ragdoll.Draw:LimbsRemoved",
501 GameAnalyticsManager.ErrorSeverity.Error,
502 "Failed to draw a ragdoll, limbs have been removed. Character: \"" +
character.
SpeciesName +
"\", removed: " +
character.
Removed +
"\n" + Environment.StackTrace.CleanupStackTrace());
509 color = Color.Lerp(Color.White, GUIStyle.Orange, (
float)Math.Sin(Timing.TotalTime * 3.5f));
513 if (!MathUtils.NearlyEqual(depthOffset, 0.0f))
517 for (
int i = 0; i < limbs.Length; i++)
521 if (!MathUtils.NearlyEqual(depthOffset, 0.0f))
533 float maxDepth = 0.0f;
534 float minDepth = 1.0f;
535 float depthOffset = 0.0f;
539 CalculateLimbDepths();
543 if (maxDepth > ladder.BackgroundSpriteDepth)
545 depthOffset = Math.Max(ladder.BackgroundSpriteDepth - 0.01f - maxDepth, 0.0f);
549 depthOffset = Math.Max(ladder.Item.GetDrawDepth() + 0.0001f - minDepth, -minDepth);
555 depthOffset = Math.Max(ladder.BackgroundSpriteDepth + 0.01f - minDepth, 0.0f);
560 CalculateLimbDepths();
564 void AdjustDepthOffset(
Item item)
566 if (item?.GetComponent<Controller>() is { ControlCharacterPose:
true, UserInCorrectPosition:
true } controller && controller.User ==
character)
568 if (controller.Item.SpriteDepth <= maxDepth || controller.DrawUserBehind)
570 depthOffset = Math.Max(controller.Item.GetDrawDepth() + 0.0001f - minDepth, -minDepth);
574 depthOffset = Math.Max(controller.Item.GetDrawDepth() - 0.0001f - maxDepth, 0.0f);
580 void CalculateLimbDepths()
585 if (activeSprite !=
null)
587 maxDepth = Math.Max(activeSprite.Depth, maxDepth);
588 minDepth = Math.Min(activeSprite.Depth, minDepth);
599 if (simplePhysicsEnabled) {
return; }
608 GUI.DrawRectangle(spriteBatch,
new Rectangle((
int)pos.X, (
int)pos.Y, 5, 5), GUIStyle.Red,
true, 0.01f);
613 GUI.DrawRectangle(spriteBatch,
new Rectangle((
int)pos.X, (
int)pos.Y, 5, 5), Color.Cyan,
true, 0.01f);
624 Vector2 pos = ConvertUnits.ToDisplayUnits(joint.WorldAnchorA);
625 GUI.DrawRectangle(spriteBatch,
new Rectangle((
int)pos.X, (
int)-pos.Y, 5, 5), Color.White,
true);
627 pos = ConvertUnits.ToDisplayUnits(joint.WorldAnchorB);
628 GUI.DrawRectangle(spriteBatch,
new Rectangle((
int)pos.X, (
int)-pos.Y, 5, 5), Color.White,
true);
642 GUI.DrawRectangle(spriteBatch,
new Rectangle((
int)pos.X - 10, (
int)pos.Y - 10, 20, 20), Color.Cyan,
false, 0.01f);
649 Vector2 pos = ConvertUnits.ToDisplayUnits(humanoid.RightHandIKPos);
650 if (humanoid.character.Submarine !=
null) { pos += humanoid.character.Submarine.DrawPosition; }
651 GUI.DrawRectangle(spriteBatch,
new Rectangle((
int)pos.X, (
int)-pos.Y, 4, 4), GUIStyle.Green,
true);
652 pos = ConvertUnits.ToDisplayUnits(humanoid.LeftHandIKPos);
653 if (humanoid.character.Submarine !=
null) { pos += humanoid.character.Submarine.DrawPosition; }
654 GUI.DrawRectangle(spriteBatch,
new Rectangle((
int)pos.X, (
int)-pos.Y, 4, 4), GUIStyle.Green,
true);
656 Vector2 aimPos = humanoid.AimSourceWorldPos;
657 aimPos.Y = -aimPos.Y;
658 GUI.DrawLine(spriteBatch, aimPos - Vector2.UnitY * 3, aimPos + Vector2.UnitY * 3, Color.Red);
659 GUI.DrawLine(spriteBatch, aimPos - Vector2.UnitX * 3, aimPos + Vector2.UnitX * 3, Color.Red);
669 prevPos.Y = -prevPos.Y;
678 currPos.Y = -currPos.Y;
680 GUI.DrawRectangle(spriteBatch,
new Rectangle((
int)currPos.X - 3, (
int)currPos.Y - 3, 6, 6), Color.Cyan * 0.6f,
true, 0.01f);
681 GUI.DrawLine(spriteBatch, prevPos, currPos, Color.Cyan * 0.6f, 0, 3);
691 displayFloorPos.Y = -displayFloorPos.Y;
692 GUI.DrawLine(spriteBatch, displayFloorPos, displayFloorPos +
new Vector2(
floorNormal.X, -
floorNormal.Y) * 50.0f, Color.Cyan * 0.5f, 0, 2);
697 GUI.DrawLine(spriteBatch,
void SelectCharacter(Character character)
bool IsRemotelyControlled
Is the character controlled remotely (either by another player, or a server-side AIController)
Item????????? SelectedItem
The primary selected item. It can be any device that character interacts with. This excludes items li...
IEnumerable< ParticleEmitter > GibEmitters
static readonly List< Character > CharacterList
UInt16 LastNetworkUpdateID
static Character? Controlled
Item SelectedSecondaryItem
The secondary selected item. It's an item other than a device (see SelectedItem), e....
bool IsVisible
Is the character currently visible on the camera. Refresh the value by calling DoVisibilityCheck.
CharacterSound GetSound(Func< CharacterSound, bool > predicate=null, bool random=false)
Note that when a predicate is provided, the random option uses Linq.Where() extension method,...
readonly AnimController AnimController
List< CharacterStateInfo > MemState
List< CharacterStateInfo > MemLocalState
readonly Character SelectedCharacter
readonly Item SelectedItem
readonly AnimController.Animation Animation
virtual Vector2 WorldPosition
static ParticleManager ParticleManager
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)
bool TryInteract(Character user, bool ignoreRequiredItems=false, bool forceSelectKey=false, bool forceUseKey=false)
Sprite GetActiveSprite(bool excludeConditionalSprites=true)
Vector2 PullJointWorldAnchorB
void Draw(SpriteBatch spriteBatch, Camera cam, Color? overrideColor=null, bool disableDeformations=false)
Vector2 PullJointWorldAnchorA
readonly ParticleEmitterPrefab Prefab
void Emit(float deltaTime, Vector2 position, Hull? hullGuess=null, float angle=0.0f, float particleRotation=0.0f, float velocityMultiplier=1.0f, float sizeMultiplier=1.0f, float amountMultiplier=1.0f, Color? colorMultiplier=null, ParticlePrefab? overrideParticle=null, bool mirrorAngle=false, Tuple< Vector2, Vector2 >? tracerPoints=null)
ParticlePrefab? ParticlePrefab
Particle CreateParticle(string prefabName, Vector2 position, float angle, float speed, Hull hullGuess=null, float collisionIgnoreTimer=0f, Tuple< Vector2, Vector2 > tracerPoints=null)
DrawTargetType DrawTarget
void MoveToTargetPosition(bool lerp=true)
void DebugDraw(SpriteBatch spriteBatch, Color color, bool forceColor=false)
void UpdateDrawPosition(bool interpolate=true)
float GetDepthOffset()
Offset added to the default draw depth of the character's limbs. For example, climbing on ladders aff...
Limb[] inversedLimbDrawOrder
Inversed draw order, which is used for drawing the limbs in 3d (deformable sprites).
void DebugDraw(SpriteBatch spriteBatch)
void PlayImpactSound(Limb limb)
void Draw(SpriteBatch spriteBatch, Camera cam)
bool IsHangingWithRope
Is hanging to something with a rope, so that can reel towards it. Currently only possible in water.
HashSet< SpriteDeformation > SpriteDeformations
void SetPosition(Vector2 simPosition, bool lerp=false, bool ignorePlatforms=true, bool forceMainLimbToCollider=false, bool moveLatchers=true)
Vector2 overrideTargetMovement
Limb GetLimb(LimbType limbType, bool excludeSevered=true)
Note that if there are multiple limbs of the same type, only the first (valid) limb is returned.