1 using Microsoft.Xna.Framework;
2 using Microsoft.Xna.Framework.Input;
3 using Microsoft.Xna.Framework.Graphics;
5 using System.Collections.Generic;
11 using FarseerPhysics.Dynamics;
38 private bool ShowExtraRagdollControls => editLimbs || editJoints;
42 private Vector2 spawnPosition;
44 private bool editCharacterInfo;
45 private bool editRagdoll;
46 private bool editAnimations;
47 private bool editLimbs;
48 private bool editJoints;
51 private bool drawSkeleton;
52 private bool drawDamageModifiers;
53 private bool showParamsEditor;
54 private bool showSpritesheet;
55 private bool isFrozen;
56 private bool autoFreeze;
57 private bool limbPairEditing;
58 private bool uniformScaling;
59 private bool lockSpriteOrigin;
60 private bool lockSpritePosition;
61 private bool lockSpriteSize;
62 private bool recalculateCollider;
63 private bool copyJointSettings;
64 private bool showColliders;
65 private bool displayWearables;
66 private bool displayBackgroundColor;
67 private bool onlyShowSourceRectForSelectedLimbs;
68 private bool unrestrictSpritesheet;
70 private enum JointCreationMode
77 private JointCreationMode jointCreationMode;
78 private bool isDrawingLimb;
81 private Limb jointStartLimb;
82 private Limb jointEndLimb;
83 private Vector2? anchor1Pos;
85 private const float holdTime = 0.2f;
86 private double holdTimer;
88 private float spriteSheetZoom = 1;
89 private float spriteSheetMinZoom = 0.25f;
90 private float spriteSheetMaxZoom = 1;
91 private const int spriteSheetOffsetY = 20;
92 private const int spriteSheetOffsetX = 30;
93 private bool hideBodySheet;
94 private Color backgroundColor =
new Color(0.2f, 0.2f, 0.2f, 1.0f);
95 private Vector2 cameraOffset;
97 private readonly List<LimbJoint> selectedJoints =
new List<LimbJoint>();
98 private readonly List<Limb> selectedLimbs =
new List<Limb>();
99 private readonly HashSet<Character> editedCharacters =
new HashSet<Character>();
101 private bool isEndlessRunner;
105 private Rectangle CalculateSpritesheetRectangle() =>
106 Textures ==
null || Textures.None() ?
Rectangle.Empty :
110 (
int)(Textures.OrderByDescending(t => t.Width).First().Width * spriteSheetZoom),
111 (
int)(Textures.Sum(t => t.Height) * spriteSheetZoom));
113 private const string screenTextTag =
"CharacterEditor.";
121 GUI.ForceMouseOn(
null);
133 CalculateMovementLimits();
134 isEndlessRunner =
true;
145 if (humanSpeciesName.IsEmpty)
165 private void ResetVariables()
167 editCharacterInfo =
false;
169 editAnimations =
false;
173 drawSkeleton =
false;
174 drawDamageModifiers =
false;
175 showParamsEditor =
false;
176 showSpritesheet =
false;
179 limbPairEditing =
false;
180 uniformScaling =
true;
181 lockSpriteOrigin =
true;
182 lockSpritePosition =
false;
183 lockSpriteSize =
false;
184 recalculateCollider =
false;
185 copyJointSettings =
false;
186 showColliders =
false;
187 displayWearables =
true;
188 displayBackgroundColor =
false;
189 jointCreationMode = JointCreationMode.None;
190 isDrawingLimb =
false;
192 cameraOffset = Vector2.Zero;
195 jointStartLimb =
null;
196 visibleSpecies =
null;
197 onlyShowSourceRectForSelectedLimbs =
false;
198 unrestrictSpritesheet =
false;
199 editedCharacters.Clear();
200 selectedJoints.Clear();
201 selectedLimbs.Clear();
202 if (character !=
null)
213 Wizard.instance?.Reset();
216 private void Reset(IEnumerable<Character> characters =
null)
218 characters ??= editedCharacters;
219 characters.ForEach(c => ResetParams(c));
223 private static void ResetParams(Character character)
225 character.Params.Reset(
true);
226 foreach (var animation
in character.AnimController.AllAnimParams)
228 animation.Reset(
true);
229 animation.ClearHistory();
231 character.AnimController.RagdollParams.Reset(
true);
232 character.AnimController.RagdollParams.ClearHistory();
233 character.ForceRun =
false;
234 character.AnimController.ForceSelectAnimationType =
AnimationType.NotDefined;
239 SoundPlayer.OverrideMusicType = Identifier.Empty;
241 GUI.ForceMouseOn(
null);
246 isEndlessRunner =
false;
248 if (character !=
null && !character.Removed)
268 private void OnResolutionChanged()
275 return TextManager.Get(screenTextTag + tag);
281 if (rightArea ==
null || leftArea ==
null) {
return; }
286 if (displayBackgroundColor)
297 Limb lastLimb = selectedLimbs.LastOrDefault();
298 if (lastLimb ==
null)
300 var lastJoint = selectedJoints.LastOrDefault();
301 if (lastJoint !=
null)
306 if (lastLimb !=
null)
319 if (editLimbs && !unrestrictSpritesheet)
323 if (ShowExtraRagdollControls)
325 createLimbButton.
Enabled = editLimbs;
326 duplicateLimbButton.
Enabled = selectedLimbs.Any();
327 deleteSelectedButton.
Enabled = selectedLimbs.Any() || selectedJoints.Any();
328 createJointButton.
Enabled = selectedLimbs.Any() || selectedJoints.Any();
334 createLimbButton.
Color = Color.Yellow;
339 createLimbButton.
Color = Color.White;
345 switch (jointCreationMode)
347 case JointCreationMode.Select:
348 case JointCreationMode.Create:
350 createJointButton.
Color = Color.Yellow;
354 createJointButton.
Color = Color.White;
359 if (showParamsEditor)
365 public override void Update(
double deltaTime)
367 base.Update(deltaTime);
372 spriteSheetRect = CalculateSpritesheetRectangle();
376 SetToggle(paramsToggle, !paramsToggle.
Selected);
382 if (GUI.KeyboardDispatcher.Subscriber ==
null)
386 SetToggle(characterInfoToggle, !characterInfoToggle.
Selected);
390 SetToggle(ragdollToggle, !ragdollToggle.
Selected);
394 SetToggle(limbsToggle, !limbsToggle.
Selected);
398 SetToggle(jointsToggle, !jointsToggle.
Selected);
402 SetToggle(animsToggle, !animsToggle.
Selected);
407 Widget.EnableMultiSelect = !editAnimations;
411 if (editJoints || editLimbs || editIK)
414 character.AnimController.ResetJoints();
415 character.AnimController.ResetLimbs();
422 CurrentAnimation.
Undo();
429 if (editJoints || editLimbs || editIK)
432 character.AnimController.ResetJoints();
433 character.AnimController.ResetLimbs();
440 CurrentAnimation.
Redo();
448 Widget.EnableMultiSelect =
false;
451 SetToggle(showCollidersToggle, !showCollidersToggle.
Selected);
455 SetToggle(lightsToggle, !lightsToggle.
Selected);
459 SetToggle(damageModifiersToggle, !damageModifiersToggle.
Selected);
463 SetToggle(skeletonToggle, !skeletonToggle.
Selected);
467 SetToggle(spritesheetToggle, !spritesheetToggle.
Selected);
471 SetToggle(ikToggle, !ikToggle.
Selected);
478 character.AnimController.Collider.PhysEnabled =
true;
481 if (animTestPoseToggle.
Enabled)
485 SetToggle(animTestPoseToggle, !animTestPoseToggle.
Selected);
490 animTestPoseToggle.
Selected =
false;
495 bool isSwimming = character.AnimController.ForceSelectAnimationType ==
AnimationType.SwimFast || character.AnimController.ForceSelectAnimationType ==
AnimationType.SwimSlow;
496 bool isMovingFast = character.AnimController.ForceSelectAnimationType ==
AnimationType.Run || character.AnimController.ForceSelectAnimationType ==
AnimationType.SwimFast;
497 if (character.AnimController.CanWalk)
524 index = isMovingFast ? 0 : 1;
529 animSelection.
Select(index);
535 bool isSwimming = character.AnimController.ForceSelectAnimationType ==
AnimationType.SwimFast || character.AnimController.ForceSelectAnimationType ==
AnimationType.SwimSlow;
548 if (selectedLimbs.Any())
550 selectedLimbs.Clear();
553 if (selectedJoints.Any())
555 selectedJoints.Clear();
556 foreach (var w
in jointSelectionWidgets.Values)
559 w.LinkedWidget?.Refresh();
567 jointCreationMode = JointCreationMode.None;
568 isDrawingLimb =
false;
578 ToggleJointCreationMode();
581 UpdateJointCreation();
582 UpdateLimbCreation();
601 holdTimer += deltaTime;
602 if (holdTimer > holdTime)
609 holdTimer += deltaTime;
610 if (holdTimer > holdTime)
617 holdTimer += deltaTime;
618 if (holdTimer > holdTime)
625 holdTimer += deltaTime;
626 if (holdTimer > holdTime)
637 float moveSpeed = (float)deltaTime * 300.0f /
Cam.
Zoom;
644 cameraOffset.Y += moveSpeed;
648 cameraOffset.X -= moveSpeed;
652 cameraOffset.Y -= moveSpeed;
656 cameraOffset.X += moveSpeed;
660 cameraOffset = Vector2.Clamp(cameraOffset, min, max);
675 if (character.IsRagdolled)
677 character.AnimController.ResetPullJoints();
679 character.ControlLocalPlayer((
float)deltaTime,
Cam,
false);
680 character.Control((
float)deltaTime,
Cam);
681 character.AnimController.UpdateAnimations((
float)deltaTime);
682 character.AnimController.UpdateRagdoll((
float)deltaTime,
Cam);
683 character.CurrentHull = character.AnimController.CurrentHull;
686 if (character.Position.X < min)
690 else if (character.Position.X > max)
699 catch (WorldLockedException e)
701 string errorMsg =
"Attempted to modify the state of the physics simulation while a time step was running.";
702 DebugConsole.ThrowError(errorMsg, e);
703 GameAnalyticsManager.AddErrorEventOnce(
"CharacterEditorScreen.Update:WorldLockedException" + e.Message, GameAnalyticsManager.ErrorSeverity.Critical, errorMsg);
707 Cam.
MoveCamera((
float)deltaTime, allowMove:
false, allowZoom: GUI.MouseOn ==
null);
708 Vector2 targetPos = character.WorldPosition;
713 moveSpeed.X = -moveSpeed.X;
714 cameraOffset += moveSpeed;
717 cameraOffset = Vector2.Clamp(cameraOffset, min, max);
722 jointSelectionWidgets.Values.ForEach(w => w.Update((
float)deltaTime));
723 limbEditWidgets.Values.ForEach(w => w.Update((
float)deltaTime));
724 animationWidgets.Values.ForEach(w => w.Update((
float)deltaTime));
728 foreach (
Limb limb
in character.AnimController.Limbs)
730 if (limb ==
null || limb.
ActiveSprite ==
null) {
continue; }
731 if (selectedJoints.Any(j => j.LimbA == limb || j.LimbB == limb)) {
continue; }
735 HandleLimbSelection(limb);
740 HandleLimbSelection(limb);
750 totalMassText.
Text = GetTotalMassText();
755 return TextManager.GetWithVariable($
"{screenTextTag}totalmass",
"[mass]", character?.
AnimController?.Mass.FormatZeroDecimal() ??
"0");
760 foreach (var limb
in character.AnimController.Limbs)
762 if (limb?.ActiveSprite ==
null) {
continue; }
763 if (selectedJoints.Any(j => j.LimbA == limb || j.LimbB == limb)) {
continue; }
776 private Vector2 scaledMouseSpeed;
777 public override void Draw(
double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
795 base.Draw(deltaTime, graphics, spriteBatch);
797 graphics.Clear(backgroundColor);
800 spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, transformMatrix:
Cam.
Transform);
806 spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, transformMatrix:
Cam.
Transform);
810 character.AnimController.DebugDraw(spriteBatch);
812 else if (showColliders)
814 character.AnimController.Collider.DebugDraw(spriteBatch, Color.White, forceColor:
true);
815 foreach (var limb
in character.AnimController.Limbs)
819 limb.body.DebugDraw(spriteBatch, GUIStyle.Green, forceColor:
true);
829 spriteBatch.Begin(SpriteSortMode.Deferred, Lights.CustomBlendStates.Multiplicative,
null, DepthStencilState.None,
null,
null,
null);
836 if (drawDamageModifiers)
838 foreach (
Limb limb
in character.AnimController.Limbs)
840 if (selectedLimbs.Contains(limb) || selectedLimbs.None())
848 DrawAnimationControls(spriteBatch, (
float)deltaTime);
852 DrawLimbEditor(spriteBatch);
854 if (drawSkeleton || editRagdoll || editJoints || editLimbs || editIK)
856 DrawRagdoll(spriteBatch, (
float)deltaTime);
859 Limb head = character.AnimController.GetLimb(
LimbType.Head);
860 if (head !=
null && character.CanEat && selectedLimbs.Contains(head))
862 var mouthPos = character.AnimController.GetMouthPosition();
863 if (mouthPos.HasValue)
865 ShapeExtensions.DrawPoint(spriteBatch, SimToScreen(mouthPos.Value), GUIStyle.Red, size: 8);
870 DrawSpritesheetEditor(spriteBatch, (
float)deltaTime);
874 GUI.DrawRectangle(spriteBatch, newLimbRect, Color.Yellow);
876 if (jointCreationMode != JointCreationMode.None)
879 if (jointCreationMode == JointCreationMode.Select)
887 if (jointStartLimb !=
null && jointStartLimb.
ActiveSprite !=
null)
889 GUI.DrawRectangle(spriteBatch, GetLimbSpritesheetRect(jointStartLimb), Color.Yellow, thickness: 3);
890 GUI.DrawRectangle(spriteBatch, GetLimbPhysicRect(jointStartLimb), Color.Yellow, thickness: 3);
892 if (jointEndLimb !=
null && jointEndLimb.
ActiveSprite !=
null)
894 GUI.DrawRectangle(spriteBatch, GetLimbSpritesheetRect(jointEndLimb), GUIStyle.Green, thickness: 3);
895 GUI.DrawRectangle(spriteBatch, GetLimbPhysicRect(jointEndLimb), GUIStyle.Green, thickness: 3);
899 if (jointStartLimb !=
null)
901 var startPos = GetLimbSpritesheetRect(jointStartLimb).Center.ToVector2();
902 var offset = anchor1Pos ?? Vector2.Zero;
910 if (jointStartLimb !=
null)
913 var offset = anchor1Pos.HasValue ? Vector2.Transform(anchor1Pos.Value, Matrix.CreateRotationZ(jointStartLimb.
Rotation)) : Vector2.Zero;
926 Vector2 indicatorPos = MiddleWall.Entities.First().DrawPosition;
927 GUI.DrawIndicator(spriteBatch, indicatorPos,
Cam, 700, GUIStyle.SubmarineLocationIcon.Value.Sprite, Color.White);
929 GUI.Draw(
Cam, spriteBatch);
938 if (selectedJoints.Count == 1)
940 GUI.DrawString(spriteBatch,
new Vector2(
GameMain.
GraphicsWidth / 2, 20), $
"{GetCharacterEditorTranslation("Selected")}: {selectedJoints.First().Params.Name}", Color.White, font: GUIStyle.LargeFont);
942 if (selectedLimbs.Count == 1)
944 GUI.DrawString(spriteBatch,
new Vector2(
GameMain.
GraphicsWidth / 2, 20), $
"{GetCharacterEditorTranslation("Selected")}: {selectedLimbs.First().Params.Name}", Color.White, font: GUIStyle.LargeFont);
948 Limb lastLimb = selectedLimbs.LastOrDefault();
949 if (lastLimb ==
null)
951 var lastJoint = selectedJoints.LastOrDefault();
952 if (lastJoint !=
null)
957 if (lastLimb !=
null)
960 bool useSpritesheetOrientation =
float.IsNaN(lastLimb.
Params.SpriteOrientation);
961 GUI.DrawString(spriteBatch,
new Vector2(topLeft.X + 350 * GUI.xScale,
GameMain.
GraphicsHeight - 95 * GUI.yScale),
GetCharacterEditorTranslation(
"SpriteOrientation")+
":", useSpritesheetOrientation ? Color.White : Color.Yellow, Color.Gray * 0.5f, 10, GUIStyle.Font);
963 DrawRadialWidget(spriteBatch,
new Vector2(topLeft.X + 610 * GUI.xScale,
GameMain.
GraphicsHeight - 75 * GUI.yScale), orientation,
967 TryUpdateSubParam(lastLimb.Params,
"spriteorientation".ToIdentifier(), angle);
968 selectedLimbs.ForEach(l => TryUpdateSubParam(l.Params,
"spriteorientation".ToIdentifier(), angle));
971 UpdateOtherLimbs(lastLimb, l => TryUpdateSubParam(l.Params,
"spriteorientation".ToIdentifier(), angle));
973 }, circleRadius: 40, widgetSize: 15, rotationOffset: 0, autoFreeze:
false, rounding: 10);
981 angle => TryUpdateRagdollParam(
"spritesheetorientation", angle), circleRadius: 40, widgetSize: 15, rotationOffset: 0, autoFreeze:
false, rounding: 10);
988 foreach (
Limb limb
in character.AnimController.Limbs)
991 GUI.DrawLine(spriteBatch, limbDrawPos + Vector2.UnitY * 5.0f, limbDrawPos - Vector2.UnitY * 5.0f, Color.White);
992 GUI.DrawLine(spriteBatch, limbDrawPos + Vector2.UnitX * 5.0f, limbDrawPos - Vector2.UnitX * 5.0f, Color.White);
995 GUI.DrawString(spriteBatch,
new Vector2(
GameMain.
GraphicsWidth / 2, 0), $
"Cursor World Pos: {character.CursorWorldPosition}", Color.White, font: GUIStyle.SmallFont);
996 GUI.DrawString(spriteBatch,
new Vector2(
GameMain.
GraphicsWidth / 2, 20), $
"Cursor Pos: {character.CursorPosition}", Color.White, font: GUIStyle.SmallFont);
997 GUI.DrawString(spriteBatch,
new Vector2(
GameMain.
GraphicsWidth / 2, 40), $
"Cursor Screen Pos: {PlayerInput.MousePosition}", Color.White, font: GUIStyle.SmallFont);
1000 var collider = character.AnimController.Collider;
1001 var colliderDrawPos = SimToScreen(collider.SimPosition);
1002 Vector2 forward = Vector2.Transform(Vector2.UnitY, Matrix.CreateRotationZ(collider.Rotation));
1003 var endPos = SimToScreen(collider.SimPosition + forward * collider.Radius);
1004 GUI.DrawLine(spriteBatch, colliderDrawPos, endPos, GUIStyle.Green);
1005 GUI.DrawLine(spriteBatch, colliderDrawPos, SimToScreen(collider.SimPosition + forward * 0.25f), Color.Blue);
1006 Vector2 left = forward.Left();
1007 GUI.DrawLine(spriteBatch, colliderDrawPos, SimToScreen(collider.SimPosition + left * 0.25f), GUIStyle.Red);
1008 ShapeExtensions.DrawCircle(spriteBatch, colliderDrawPos, (endPos - colliderDrawPos).Length(), 40, GUIStyle.Green);
1009 GUI.DrawString(spriteBatch,
new Vector2(
GameMain.
GraphicsWidth - 300, 0), $
"Collider rotation: {MathHelper.ToDegrees(MathUtils.WrapAngleTwoPi(collider.Rotation))}", Color.White, font: GUIStyle.SmallFont);
1015 #region Ragdoll Manipulation
1016 private void UpdateJointCreation()
1018 if (jointCreationMode == JointCreationMode.None)
1020 jointStartLimb =
null;
1021 jointEndLimb =
null;
1027 var selectedJoint = selectedJoints.LastOrDefault();
1028 if (selectedJoint !=
null)
1030 if (jointCreationMode == JointCreationMode.Create)
1032 if (spriteSheetRect.Contains(PlayerInput.MousePosition))
1034 jointEndLimb = GetClosestLimbOnSpritesheet(PlayerInput.MousePosition, l => l !=
null && l != jointStartLimb && l.ActiveSprite !=
null);
1035 if (jointEndLimb !=
null && PlayerInput.PrimaryMouseButtonClicked())
1037 Vector2 anchor1 = anchor1Pos.HasValue ? anchor1Pos.Value / spriteSheetZoom : Vector2.Zero;
1038 anchor1.X = -anchor1.X;
1039 Vector2 anchor2 = (GetLimbSpritesheetRect(jointEndLimb).Center.ToVector2() - PlayerInput.MousePosition) / spriteSheetZoom;
1040 anchor2.X = -anchor2.X;
1041 CreateJoint(jointStartLimb.
Params.ID, jointEndLimb.
Params.ID, anchor1, anchor2);
1042 jointCreationMode = JointCreationMode.None;
1047 jointEndLimb = GetClosestLimbOnRagdoll(PlayerInput.MousePosition, l => l !=
null && l != jointStartLimb && l.ActiveSprite !=
null);
1048 if (jointEndLimb !=
null && PlayerInput.PrimaryMouseButtonClicked())
1050 Vector2 anchor2 = ConvertUnits.ToDisplayUnits(jointEndLimb.
body.
FarseerBody.GetLocalPoint(ScreenToSim(PlayerInput.MousePosition)));
1051 CreateJoint(jointStartLimb.
Params.ID, jointEndLimb.
Params.ID, anchor1Pos, anchor2);
1052 jointCreationMode = JointCreationMode.None;
1058 jointStartLimb = selectedJoint.LimbB;
1059 if (spriteSheetRect.Contains(PlayerInput.MousePosition))
1061 anchor1Pos = GetLimbSpritesheetRect(jointStartLimb).Center.ToVector2() - PlayerInput.MousePosition;
1065 anchor1Pos = ConvertUnits.ToDisplayUnits(jointStartLimb.
body.
FarseerBody.GetLocalPoint(ScreenToSim(PlayerInput.MousePosition)));
1067 if (PlayerInput.PrimaryMouseButtonClicked())
1069 jointCreationMode = JointCreationMode.Create;
1075 jointCreationMode = JointCreationMode.None;
1080 if (selectedLimbs.Any())
1082 if (spriteSheetRect.Contains(PlayerInput.MousePosition))
1084 if (jointCreationMode == JointCreationMode.Create)
1086 jointEndLimb = GetClosestLimbOnSpritesheet(PlayerInput.MousePosition, l => l !=
null && l != jointStartLimb && l.ActiveSprite !=
null && !l.Hidden);
1087 if (jointEndLimb !=
null && PlayerInput.PrimaryMouseButtonClicked())
1089 Vector2 anchor1 = anchor1Pos.HasValue ? anchor1Pos.Value / spriteSheetZoom : Vector2.Zero;
1090 anchor1.X = -anchor1.X;
1091 Vector2 anchor2 = (GetLimbSpritesheetRect(jointEndLimb).Center.ToVector2() - PlayerInput.MousePosition) / spriteSheetZoom;
1092 anchor2.X = -anchor2.X;
1093 CreateJoint(jointStartLimb.
Params.ID, jointEndLimb.
Params.ID, anchor1, anchor2);
1094 jointCreationMode = JointCreationMode.None;
1097 else if (PlayerInput.PrimaryMouseButtonClicked())
1099 jointStartLimb = GetClosestLimbOnSpritesheet(PlayerInput.MousePosition, l => selectedLimbs.Contains(l));
1100 if (jointStartLimb !=
null)
1102 anchor1Pos = GetLimbSpritesheetRect(jointStartLimb).Center.ToVector2() - PlayerInput.MousePosition;
1103 jointCreationMode = JointCreationMode.Create;
1109 if (jointCreationMode == JointCreationMode.Create)
1111 jointEndLimb = GetClosestLimbOnRagdoll(PlayerInput.MousePosition, l => l !=
null && l != jointStartLimb && l.ActiveSprite !=
null && !l.Hidden);
1112 if (jointEndLimb !=
null && PlayerInput.PrimaryMouseButtonClicked())
1114 Vector2 anchor1 = anchor1Pos ?? Vector2.Zero;
1115 Vector2 anchor2 = ConvertUnits.ToDisplayUnits(jointEndLimb.
body.
FarseerBody.GetLocalPoint(ScreenToSim(PlayerInput.MousePosition)));
1116 CreateJoint(jointStartLimb.
Params.ID, jointEndLimb.
Params.ID, anchor1, anchor2);
1117 jointCreationMode = JointCreationMode.None;
1120 else if (PlayerInput.PrimaryMouseButtonClicked())
1122 jointStartLimb = GetClosestLimbOnRagdoll(PlayerInput.MousePosition, l => selectedLimbs.Contains(l) && !l.Hidden);
1123 if (jointStartLimb !=
null)
1125 anchor1Pos = ConvertUnits.ToDisplayUnits(jointStartLimb.
body.
FarseerBody.GetLocalPoint(ScreenToSim(PlayerInput.MousePosition)));
1126 jointCreationMode = JointCreationMode.Create;
1133 jointCreationMode = JointCreationMode.None;
1138 private void UpdateLimbCreation()
1147 SetToggle(limbsToggle,
true);
1149 if (PlayerInput.PrimaryMouseButtonHeld())
1153 newLimbRect =
new Rectangle((
int)PlayerInput.MousePosition.X, (
int)PlayerInput.MousePosition.Y, 0, 0);
1157 newLimbRect.Size =
new Point((
int)PlayerInput.MousePosition.X - newLimbRect.X, (
int)PlayerInput.MousePosition.Y - newLimbRect.Y);
1159 newLimbRect.Size =
new Point(Math.Max(newLimbRect.Width, 2), Math.Max(newLimbRect.Height, 2));
1161 if (PlayerInput.PrimaryMouseButtonClicked())
1164 newLimbRect.Location =
new Point(newLimbRect.X - spriteSheetOffsetX, newLimbRect.Y - spriteSheetOffsetY);
1165 newLimbRect = newLimbRect.Divide(spriteSheetZoom);
1166 CreateNewLimb(newLimbRect);
1167 isDrawingLimb =
false;
1172 private void CopyLimb(Limb limb)
1174 if (limb ==
null) {
return; }
1176 var rect = limb.ActiveSprite.SourceRect;
1177 var spriteParams = limb.Params.GetSprite();
1178 var newLimbElement =
new XElement(
"limb",
1179 new XAttribute(
"id", RagdollParams.Limbs.Last().ID + 1),
1180 new XAttribute(
"radius", limb.Params.Radius),
1181 new XAttribute(
"width", limb.Params.Width),
1182 new XAttribute(
"height", limb.Params.Height),
1183 new XElement(
"sprite",
1184 new XAttribute(
"texture", spriteParams.Texture),
1185 new XAttribute(
"sourcerect", $
"{rect.X}, {rect.Y}, {rect.Size.X}, {rect.Size.Y}"))).FromPackage(character.Prefab.ContentPackage);
1186 CreateLimb(newLimbElement);
1189 private void CreateNewLimb(Rectangle sourceRect)
1191 var newLimbElement =
new XElement(
"limb",
1192 new XAttribute(
"id", RagdollParams.Limbs.Last().ID + 1),
1193 new XAttribute(
"width", sourceRect.Width * RagdollParams.TextureScale),
1194 new XAttribute(
"height", sourceRect.Height * RagdollParams.TextureScale),
1195 new XElement(
"sprite",
1196 new XAttribute(
"texture", RagdollParams.Limbs.First().GetSprite().Texture),
1197 new XAttribute(
"sourcerect", $
"{sourceRect.X}, {sourceRect.Y}, {sourceRect.Width}, {sourceRect.Height}"))).FromPackage(character.Prefab.ContentPackage);
1198 CreateLimb(newLimbElement);
1199 lockSpriteOriginToggle.Selected =
false;
1200 recalculateColliderToggle.Selected =
true;
1203 private void CreateLimb(ContentXElement newElement)
1205 if (RagdollParams.MainElement ==
null)
1207 DebugConsole.ThrowError(
"Main element null! Failed to create a limb.");
1210 var lastElement = RagdollParams.MainElement.GetChildElements(
"limb").LastOrDefault();
1211 if (lastElement !=
null)
1213 lastElement.AddAfterSelf(newElement);
1217 RagdollParams.MainElement.AddFirst(newElement);
1219 var newLimbParams =
new RagdollParams.LimbParams(newElement, RagdollParams);
1220 RagdollParams.Limbs.Add(newLimbParams);
1221 character.AnimController.Recreate();
1223 TeleportTo(spawnPosition);
1226 selectedLimbs.Add(character.AnimController.Limbs.Single(l => l.Params == newLimbParams));
1227 ResetParamsEditor();
1233 private void CreateJoint(
int fromLimb,
int toLimb, Vector2? anchor1 =
null, Vector2? anchor2 =
null)
1235 if (RagdollParams.Joints.Any(j => j.Limb1 == fromLimb && j.Limb2 == toLimb))
1237 DebugConsole.ThrowErrorLocalized(GetCharacterEditorTranslation(
"ExistingJointFound").Replace(
"[limbid1]", fromLimb.ToString()).Replace(
"[limbid2]", toLimb.ToString()));
1240 if (RagdollParams.MainElement ==
null)
1242 DebugConsole.ThrowError(
"The main element of the ragdoll params is null! Failed to create a joint.");
1246 Vector2 a1 = anchor1 ?? Vector2.Zero;
1247 Vector2 a2 = anchor2 ?? Vector2.Zero;
1248 var newJointElement =
new XElement(
"joint",
1249 new XAttribute(
"limb1", fromLimb),
1250 new XAttribute(
"limb2", toLimb),
1251 new XAttribute(
"limb1anchor", $
"{a1.X.Format(2)}, {a1.Y.Format(2)}"),
1252 new XAttribute(
"limb2anchor", $
"{a2.X.Format(2)}, {a2.Y.Format(2)}")
1253 ).FromPackage(character.Prefab.ContentPackage);
1254 var lastJointElement = RagdollParams.MainElement.GetChildElements(
"joint").LastOrDefault() ?? RagdollParams.MainElement.GetChildElements(
"limb").LastOrDefault();
1255 if (lastJointElement ==
null)
1257 DebugConsole.ThrowErrorLocalized(GetCharacterEditorTranslation(
"CantAddJointsNoLimbElements"));
1260 lastJointElement.AddAfterSelf(newJointElement);
1261 var newJointParams =
new RagdollParams.JointParams(newJointElement, RagdollParams);
1262 RagdollParams.Joints.Add(newJointParams);
1263 character.AnimController.Recreate();
1265 TeleportTo(spawnPosition);
1268 SetToggle(jointsToggle,
true);
1269 selectedJoints.Add(character.AnimController.LimbJoints.Single(j => j.Params == newJointParams));
1275 private void DeleteSelected()
1278 for (
int i = 0; i < selectedJoints.Count; i++)
1280 var joint = selectedJoints[i];
1281 joint.Params.Element.Remove();
1282 RagdollParams.Joints.Remove(joint.Params);
1284 var removedIDs =
new List<int>();
1285 for (
int i = 0; i < selectedLimbs.Count; i++)
1287 if (character.IsHumanoid)
1289 DebugConsole.ThrowErrorLocalized(GetCharacterEditorTranslation(
"HumanoidLimbDeletionDisabled"));
1292 var limb = selectedLimbs[i];
1293 if (limb == character.AnimController.MainLimb)
1295 DebugConsole.ThrowError(
"Can't remove the main limb, because it will cause unreveratable issues.");
1298 removedIDs.Add(limb.Params.ID);
1299 limb.Params.Element.Remove();
1300 RagdollParams.Limbs.Remove(limb.Params);
1303 var renamedIDs =
new Dictionary<int, int>();
1304 for (
int i = 0; i < RagdollParams.Limbs.Count; i++)
1306 int oldID = RagdollParams.Limbs[i].ID;
1310 var limbParams = RagdollParams.Limbs[i];
1311 limbParams.ID = newID;
1312 limbParams.Name = limbParams.GenerateName();
1313 renamedIDs.Add(oldID, newID);
1317 var jointsToRemove =
new List<RagdollParams.JointParams>();
1318 for (
int i = 0; i < RagdollParams.Joints.Count; i++)
1320 var joint = RagdollParams.Joints[i];
1321 if (removedIDs.Contains(joint.Limb1) || removedIDs.Contains(joint.Limb2))
1324 jointsToRemove.Add(joint);
1329 bool rename =
false;
1330 if (renamedIDs.TryGetValue(joint.Limb1, out
int newID1))
1332 joint.Limb1 = newID1;
1335 if (renamedIDs.TryGetValue(joint.Limb2, out
int newID2))
1337 joint.Limb2 = newID2;
1342 joint.Name = joint.GenerateName();
1346 foreach (var jointParam
in jointsToRemove)
1348 jointParam.Element.Remove();
1349 RagdollParams.Joints.Remove(jointParam);
1355 #region Endless runner
1358 private void CalculateMovementLimits()
1360 min = MiddleWall.Entities.Select(w => w.Rect.Left).OrderBy(p => p).First();
1361 max = MiddleWall.Entities.Select(w => w.Rect.Right).OrderBy(p => p).Last();
1364 private readonly WallGroup[] wallGroups =
new WallGroup[3];
1366 private WallGroup MiddleWall => wallGroups[1];
1368 private IEnumerable<MapEntity> AllStructures => wallGroups.SelectMany(c => c.Entities);
1370 private class WallGroup
1372 public readonly List<MapEntity> Entities;
1374 public WallGroup(List<MapEntity> entities)
1376 Entities = entities;
1379 public WallGroup Clone()
1381 var clones =
new List<MapEntity>();
1382 Entities.ForEachMod(w => clones.Add(w.Clone()));
1383 return new WallGroup(clones);
1387 private void CloneWalls()
1389 var originalWall = wallGroups[0];
1390 int moveAmount = originalWall.Entities.FirstOrDefault(e => e is Structure).Rect.Width;
1391 for (
int i = 1; i <= 2; i++)
1393 wallGroups[i] = originalWall.Clone();
1394 foreach (var entity
in wallGroups[i].Entities)
1396 entity.Move(
new Vector2(moveAmount * i, 0));
1401 private void UpdateWalls(
bool right)
1403 int moveAmount = wallGroups[0].Entities.FirstOrDefault(e => e is Structure).Rect.Width;
1404 int amount = right ? moveAmount : -moveAmount;
1405 foreach (var wallGroup
in wallGroups)
1407 foreach (var entity
in wallGroup.Entities)
1409 entity.Move(
new Vector2(amount, 0));
1413 CalculateMovementLimits();
1415 GameMain.World.ProcessChanges();
1418 private bool wallCollisionsEnabled;
1419 private void SetWallCollisions(
bool enabled)
1421 if (!isEndlessRunner) {
return; }
1422 wallCollisionsEnabled = enabled;
1423 var collisionCategory = enabled ? FarseerPhysics.Dynamics.Category.Cat1 : FarseerPhysics.Dynamics.Category.None;
1424 AllStructures.ForEach(w => (w as Structure)?.SetCollisionCategory(collisionCategory));
1425 GameMain.World.ProcessChanges();
1429 #region Character spawning
1430 private int characterIndex = -1;
1431 private Identifier currentCharacterIdentifier;
1432 private Identifier selectedJob = Identifier.Empty;
1434 private List<Identifier> visibleSpecies;
1435 private List<Identifier> VisibleSpecies
1439 visibleSpecies ??= CharacterPrefab.Prefabs.Where(ShowCreature).OrderBy(p => p.Identifier).Select(p => p.Identifier).ToList();
1440 return visibleSpecies;
1444 private bool ShowCreature(CharacterPrefab prefab)
1446 Identifier speciesName = prefab.Identifier;
1447 if (speciesName == CharacterPrefab.HumanSpeciesName) {
return true; }
1448 if (!VanillaCharacters.Contains(prefab.ContentFile))
1453 if (CreatureMetrics.UnlockAll) {
return true; }
1454 return CreatureMetrics.Unlocked.Contains(speciesName);
1457 private IEnumerable<CharacterFile> vanillaCharacters;
1458 private IEnumerable<CharacterFile> VanillaCharacters
1462 vanillaCharacters ??= GameMain.VanillaContent.GetFiles<CharacterFile>();
1463 return vanillaCharacters;
1467 private Identifier GetNextCharacterIdentifier()
1469 GetCurrentCharacterIndex();
1471 currentCharacterIdentifier = VisibleSpecies[characterIndex];
1472 return currentCharacterIdentifier;
1475 private Identifier GetPreviousCharacterIdentifier()
1477 GetCurrentCharacterIndex();
1479 currentCharacterIdentifier = VisibleSpecies[characterIndex];
1480 return currentCharacterIdentifier;
1483 private void GetCurrentCharacterIndex()
1485 characterIndex = VisibleSpecies.IndexOf(character.SpeciesName);
1488 private void IncreaseIndex()
1491 if (characterIndex > VisibleSpecies.Count - 1)
1497 private void ReduceIndex()
1500 if (characterIndex < 0)
1502 characterIndex = VisibleSpecies.Count - 1;
1508 DebugConsole.NewMessage(GetCharacterEditorTranslation(
"TryingToSpawnCharacter").Replace(
"[config]", speciesName.ToString()), Color.HotPink);
1510 bool followCursor =
false;
1511 if (character !=
null)
1513 followCursor = character.FollowCursor;
1515 CurrentAnimation.ClearHistory();
1516 if (!character.Removed)
1525 character =
Character.
Create(speciesName, spawnPosition, ToolBox.RandomSeed(8), characterInfo, hasAi:
false, ragdoll: ragdoll);
1528 if (displayWearables)
1532 selectedJob = characterInfo.Job.Prefab.Identifier;
1536 character =
Character.
Create(speciesName, spawnPosition, ToolBox.RandomSeed(8), hasAi:
false, ragdoll: ragdoll);
1537 selectedJob = Identifier.Empty;
1539 if (character !=
null)
1543 if (character ==
null)
1545 if (currentCharacterIdentifier == speciesName)
1552 SpawnCharacter(currentCharacterIdentifier);
1559 private void OnPreSpawn()
1561 cameraOffset = Vector2.Zero;
1563 if (!isEndlessRunner)
1571 private void OnPostSpawn()
1573 currentCharacterIdentifier = character.SpeciesName;
1574 GetCurrentCharacterIndex();
1575 character.Submarine = Submarine.MainSub;
1576 character.AnimController.forceStanding = character.AnimController.CanWalk;
1577 character.AnimController.ForceSelectAnimationType = character.AnimController.CanWalk ?
AnimationType.Walk :
AnimationType.SwimSlow;
1578 Character.Controlled = character;
1579 SetWallCollisions(character.AnimController.forceStanding);
1584 ResetParamsEditor();
1585 CurrentAnimation.StoreSnapshot();
1586 RagdollParams.StoreSnapshot();
1587 Cam.Position = character.WorldPosition;
1588 editedCharacters.Add(character);
1591 private void ClearWidgets()
1593 Widget.SelectedWidgets.Clear();
1594 animationWidgets.Clear();
1595 jointSelectionWidgets.Clear();
1596 limbEditWidgets.Clear();
1599 private void ClearSelection()
1601 selectedLimbs.Clear();
1602 selectedJoints.Clear();
1603 foreach (var w
in jointSelectionWidgets.Values)
1606 w.LinkedWidget?.Refresh();
1610 private void RecreateRagdoll(RagdollParams ragdoll =
null)
1612 RagdollParams.Apply();
1613 character.AnimController.Recreate(ragdoll);
1614 TeleportTo(spawnPosition);
1616 var selectedJointParams = selectedJoints.Select(j => j.Params).ToList();
1617 var selectedLimbParams = selectedLimbs.Select(l => l.Params).ToList();
1621 foreach (var joint
in character.AnimController.LimbJoints)
1623 if (selectedJointParams.Contains(joint.Params))
1625 selectedJoints.Add(joint);
1628 foreach (var limb
in character.AnimController.Limbs)
1630 if (selectedLimbParams.Contains(limb.Params))
1632 selectedLimbs.Add(limb);
1635 ResetParamsEditor();
1638 private void TeleportTo(Vector2 position)
1640 if (isEndlessRunner)
1642 character.AnimController.SetPosition(ConvertUnits.ToSimUnits(position),
false);
1646 character.TeleportTo(position);
1648 Cam.Position = character.WorldPosition;
1651 public bool CreateCharacter(Identifier name,
string mainFolder,
bool isHumanoid,
ContentPackage contentPackage, XElement ragdoll, XElement config =
null, IEnumerable<AnimationParams> animations =
null)
1655 throw new ArgumentException(
"Name cannot be empty.");
1660 if (contentPackage ==
null)
1662 contentPackage = ContentPackageManager.EnabledPackages.All.LastOrDefault(cp => cp != vanilla);
1664 if (contentPackage ==
null)
1667 DebugConsole.ThrowErrorLocalized(GetCharacterEditorTranslation(
"NoContentPackageSelected"));
1670 if (vanilla !=
null && contentPackage == vanilla)
1672 GUI.AddMessage(GetCharacterEditorTranslation(
"CannotEditVanillaCharacters"), GUIStyle.Red, font: GUIStyle.LargeFont);
1676 if (contentPackage is
RegularPackage regular && !ContentPackageManager.EnabledPackages.Regular.Contains(regular))
1678 ContentPackageManager.EnabledPackages.EnableRegular(regular);
1680 GameSettings.SaveCurrentConfig();
1683 string configFilePath = Path.Combine(mainFolder, $
"{name}.xml").Replace(
@"\",
@"/");
1685 XElement overrideElement =
null;
1686 if (duplicate !=
null)
1688 visibleSpecies =
null;
1689 if (!File.Exists(configFilePath))
1693 overrideElement =
new XElement(
"override");
1699 config =
new XElement(
"Character",
1700 new XAttribute(
"speciesname", name),
1701 new XAttribute(
"humanoid", isHumanoid),
1702 new XElement(
"ragdolls", CreateRagdollPath()),
1703 new XElement(
"animations", CreateAnimationPath()),
1704 new XElement(
"health"),
1705 new XElement(
"ai"));
1709 config.TrySetAttributeValue(
"speciesname", name);
1710 config.TrySetAttributeValue(
"humanoid", isHumanoid);
1711 var ragdollElement = config.GetChildElement(
"ragdolls");
1712 if (ragdollElement ==
null)
1714 config.Add(
new XElement(
"ragdolls", CreateRagdollPath()));
1718 var path = ragdollElement.GetAttributeString(
"folder",
"");
1719 if (!
string.IsNullOrEmpty(path) && !path.Equals(
"default", StringComparison.OrdinalIgnoreCase))
1721 ragdollElement.ReplaceWith(
new XElement(
"ragdolls", CreateRagdollPath()));
1724 var animationElement = config.GetChildElement(
"animations");
1725 if (animationElement ==
null)
1727 config.Add(
new XElement(
"animations", CreateAnimationPath()));
1731 var path = animationElement.GetAttributeString(
"folder",
"");
1732 if (!
string.IsNullOrEmpty(path) && !path.Equals(
"default", StringComparison.OrdinalIgnoreCase))
1734 animationElement.ReplaceWith(
new XElement(
"animations", CreateAnimationPath()));
1739 XAttribute CreateRagdollPath() =>
new XAttribute(
"folder", Path.Combine(mainFolder, $
"Ragdolls/").Replace(
@"\",
@"/"));
1740 XAttribute CreateAnimationPath() =>
new XAttribute(
"folder", Path.Combine(mainFolder, $
"Animations/").Replace(
@"\",
@"/"));
1742 if (overrideElement !=
null)
1744 overrideElement.Add(config);
1745 config = overrideElement;
1747 XDocument doc =
new XDocument(config);
1750 Directory.CreateDirectory(Path.GetDirectoryName(configFileContentPath.
Value));
1752 doc.Save(configFileContentPath.
Value);
1754 doc.SaveSafe(configFileContentPath.
Value);
1757 var modProject =
new ModProject(contentPackage);
1759 modProject.AddFile(newFile);
1760 modProject.Save(contentPackage.
Path);
1762 var reloadResult = ContentPackageManager.ReloadContentPackage(contentPackage);
1763 if (!reloadResult.TryUnwrapSuccess(out var newPackage))
1765 throw new Exception($
"Failed to reload package",
1766 reloadResult.TryUnwrapFailure(out var exception) ? exception :
null);
1768 contentPackage = newPackage;
1770 DebugConsole.NewMessage(GetCharacterEditorTranslation(
"ContentPackageSaved").Replace(
"[path]", contentPackage.
Path));
1782 if (animations !=
null)
1784 if (!Directory.Exists(animFolder))
1786 Directory.CreateDirectory(animFolder);
1788 foreach (var animation
in animations)
1790 XElement element = animation.MainElement;
1791 if (element ==
null) {
continue; }
1792 element.SetAttributeValue(
"type", name);
1796 element.Save(fullPath);
1798 element.SaveSafe(fullPath);
1810 if (!ragdollParams.
CanWalk) {
continue; }
1813 if (!ragdollParams.
CanWalk || !isHumanoid) {
continue; }
1825 if (!VisibleSpecies.Contains(name))
1827 VisibleSpecies.Add(name);
1829 SpawnCharacter(name, ragdollParams);
1830 limbPairEditing =
false;
1831 limbsToggle.Selected =
true;
1832 recalculateColliderToggle.Selected =
true;
1833 lockSpriteOriginToggle.Selected =
false;
1834 selectedLimbs.Add(character.AnimController.Limbs.First());
1838 private void ShowWearables()
1840 if (character.Inventory ==
null) {
return; }
1841 foreach (var item
in character.Inventory.AllItems)
1844 if (item.AllowedSlots.Contains(
InvSlotType.Head) || item.AllowedSlots.Contains(
InvSlotType.Headset)) {
continue; }
1845 item.Equip(character);
1849 private void HideWearables()
1851 character.Inventory?.AllItemsMod.ForEach(i => i.Unequip(character));
1856 private static Vector2 innerScale =
new Vector2(0.95f, 0.95f);
1858 private GUILayoutGroup rightArea, leftArea;
1859 private GUIFrame centerArea;
1861 private GUITextBlock totalMassText;
1862 private GUIFrame characterSelectionPanel;
1863 private GUIFrame fileEditPanel;
1864 private GUIFrame modesPanel;
1865 private GUIFrame buttonsPanel;
1866 private GUIFrame optionsPanel;
1867 private GUIFrame minorModesPanel;
1869 private GUIFrame ragdollControls;
1870 private GUIFrame jointControls;
1871 private GUIFrame animationControls;
1872 private GUIFrame limbControls;
1873 private GUIFrame spriteSheetControls;
1874 private GUIFrame backgroundColorPanel;
1876 private GUIDropDown animSelection;
1877 private GUITickBox freezeToggle;
1878 private GUITickBox animTestPoseToggle;
1879 private GUITickBox showCollidersToggle;
1880 private GUIScrollBar jointScaleBar;
1881 private GUIScrollBar limbScaleBar;
1882 private GUIScrollBar spriteSheetZoomBar;
1883 private GUITickBox copyJointsToggle;
1884 private GUITickBox recalculateColliderToggle;
1885 private GUIFrame resetSpriteOrientationButtonParent;
1887 private GUITickBox characterInfoToggle;
1888 private GUITickBox ragdollToggle;
1889 private GUITickBox animsToggle;
1890 private GUITickBox limbsToggle;
1891 private GUITickBox paramsToggle;
1892 private GUITickBox jointsToggle;
1893 private GUITickBox spritesheetToggle;
1894 private GUITickBox skeletonToggle;
1895 private GUITickBox lightsToggle;
1896 private GUITickBox damageModifiersToggle;
1897 private GUITickBox ikToggle;
1898 private GUITickBox lockSpriteOriginToggle;
1900 private GUIFrame extraRagdollControls;
1901 private GUIButton createJointButton;
1902 private GUIButton createLimbButton;
1903 private GUIButton deleteSelectedButton;
1904 private GUIButton duplicateLimbButton;
1906 private ToggleButton modesToggle;
1907 private ToggleButton minorModesToggle;
1908 private ToggleButton buttonsPanelToggle;
1909 private ToggleButton optionsToggle;
1910 private ToggleButton characterPanelToggle;
1911 private ToggleButton fileEditToggle;
1913 private void CreateGUI()
1916 if (rightArea !=
null)
1918 rightArea.RectTransform.Parent =
null;
1920 if (centerArea !=
null)
1922 centerArea.RectTransform.Parent =
null;
1924 if (leftArea !=
null)
1926 leftArea.RectTransform.Parent =
null;
1930 rightArea =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.15f, 1.0f), parent: Frame.RectTransform, anchor:
Anchor.CenterRight), childAnchor:
Anchor.BottomRight)
1932 RelativeSpacing = 0.02f
1934 centerArea =
new GUIFrame(
new RectTransform(
new Vector2(0.5f, 0.95f), parent: Frame.RectTransform, anchor:
Anchor.TopRight)
1936 AbsoluteOffset = new Point((int)(rightArea.RectTransform.ScaledSize.X + rightArea.RectTransform.RelativeOffset.X * rightArea.RectTransform.Parent.ScaledSize.X + (int)(20 * GUI.xScale)), (int)(20 * GUI.yScale))
1938 { CanBeFocused =
false };
1939 leftArea =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.15f, 0.95f), parent: Frame.RectTransform, anchor:
Anchor.CenterLeft), childAnchor:
Anchor.BottomLeft)
1941 RelativeSpacing = 0.02f
1943 Vector2 toggleSize =
new Vector2(1.0f, 0.03f);
1944 CreateFileEditPanel();
1945 CreateOptionsPanel(toggleSize);
1946 CreateCharacterSelectionPanel();
1947 if (rightArea.RectTransform.Children.Sum(c => c.Rect.Height) > GameMain.GraphicsHeight)
1949 fileEditPanel.GetAllChildren().Where(c => c is GUIButton).ForEach(b => b.RectTransform.MinSize = ((GUIButton)b).Frame.RectTransform.MinSize = b.RectTransform.MinSize.Multiply(
new Vector2(1.0f, 0.75f)));
1950 fileEditPanel.RectTransform.MinSize =
new Point(0, (
int)(fileEditPanel.GetChild<GUILayoutGroup>().RectTransform.Children.Sum(c => c.Rect.Height) / innerScale.Y));
1951 optionsPanel.GetAllChildren().Where(c => c is GUITickBox).ForEach(t => t.RectTransform.MinSize = t.RectTransform.MinSize.Multiply(
new Vector2(1.0f, 0.75f)));
1952 optionsPanel.RectTransform.MinSize =
new Point(0, (
int)(optionsPanel.GetChild<GUILayoutGroup>().RectTransform.Children.Sum(c => c.Rect.Height) / innerScale.Y));
1953 rightArea.Recalculate();
1955 CreateButtonsPanel();
1956 CreateModesPanel(toggleSize);
1957 CreateMinorModesPanel(toggleSize);
1958 CreateContextualControls();
1959 totalMassText =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.01f), rightArea.RectTransform), GetTotalMassText());
1962 private void CreateMinorModesPanel(Vector2 toggleSize)
1964 minorModesPanel =
new GUIFrame(
new RectTransform(
new Vector2(1.0f, 0.25f), leftArea.RectTransform));
1965 var layoutGroup =
new GUILayoutGroup(
new RectTransform(innerScale, minorModesPanel.RectTransform,
Anchor.Center))
1967 AbsoluteSpacing = 2,
1970 new GUITextBlock(
new RectTransform(
new Vector2(0.03f, 0.0f), layoutGroup.RectTransform), GetCharacterEditorTranslation(
"MinorModesTitle"), font: GUIStyle.LargeFont);
1971 paramsToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"ShowParameters")) {
Selected = showParamsEditor };
1972 paramsToggle.OnSelected = box =>
1974 showParamsEditor = box.Selected;
1977 spritesheetToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"ShowSpriteSheet")) {
Selected = showSpritesheet };
1978 spritesheetToggle.OnSelected = box =>
1980 showSpritesheet = box.Selected;
1983 showCollidersToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"ShowColliders"))
1988 showColliders = box.Selected;
1992 ikToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"EditIKTargets")) {
Selected = editIK };
1993 ikToggle.OnSelected = box =>
1995 editIK = box.Selected;
1998 skeletonToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"DrawSkeleton")) {
Selected = drawSkeleton };
1999 skeletonToggle.OnSelected = box =>
2001 drawSkeleton = box.Selected;
2004 lightsToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"EnableLights")) {
Selected = GameMain.LightManager.LightingEnabled };
2005 lightsToggle.OnSelected = box =>
2007 GameMain.LightManager.LightingEnabled = box.Selected;
2010 damageModifiersToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"DrawDamageModifiers")) {
Selected = drawDamageModifiers };
2011 damageModifiersToggle.OnSelected = box =>
2013 drawDamageModifiers = box.Selected;
2016 minorModesToggle =
new ToggleButton(
new RectTransform(
new Vector2(0.08f, 1), minorModesPanel.RectTransform,
Anchor.CenterRight,
Pivot.CenterLeft),
Direction.Left);
2017 minorModesPanel.RectTransform.MinSize =
new Point(0, (
int)(layoutGroup.RectTransform.Children.Sum(c => c.MinSize.Y + layoutGroup.AbsoluteSpacing) * 1.2f));
2020 private void CreateModesPanel(Vector2 toggleSize)
2022 modesPanel =
new GUIFrame(
new RectTransform(
new Vector2(1.0f, 0.2f), leftArea.RectTransform));
2023 var layoutGroup =
new GUILayoutGroup(
new RectTransform(innerScale, modesPanel.RectTransform,
Anchor.Center))
2025 AbsoluteSpacing = 2,
2028 new GUITextBlock(
new RectTransform(
new Vector2(0.03f, 0.0f), layoutGroup.RectTransform), GetCharacterEditorTranslation(
"ModesPanel"), font: GUIStyle.LargeFont);
2029 characterInfoToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"EditCharacter")) {
Selected = editCharacterInfo };
2030 ragdollToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"EditRagdoll")) {
Selected = editRagdoll };
2031 limbsToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"EditLimbs")) {
Selected = editLimbs };
2032 jointsToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"EditJoints")) {
Selected = editJoints };
2033 animsToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"EditAnimations")) {
Selected = editAnimations };
2034 animsToggle.OnSelected = box =>
2036 editAnimations = box.Selected;
2039 SetToggle(limbsToggle,
false);
2040 SetToggle(jointsToggle,
false);
2041 SetToggle(ragdollToggle,
false);
2042 SetToggle(characterInfoToggle,
false);
2043 spritesheetToggle.Selected =
false;
2046 ResetParamsEditor();
2049 limbsToggle.OnSelected = box =>
2051 editLimbs = box.Selected;
2054 SetToggle(animsToggle,
false);
2055 SetToggle(jointsToggle,
false);
2056 SetToggle(ragdollToggle,
false);
2057 SetToggle(characterInfoToggle,
false);
2058 spritesheetToggle.Selected =
true;
2061 ResetParamsEditor();
2064 jointsToggle.OnSelected = box =>
2066 editJoints = box.Selected;
2069 SetToggle(limbsToggle,
false);
2070 SetToggle(animsToggle,
false);
2071 SetToggle(ragdollToggle,
false);
2072 SetToggle(characterInfoToggle,
false);
2073 ikToggle.Selected =
false;
2074 spritesheetToggle.Selected =
true;
2077 ResetParamsEditor();
2080 ragdollToggle.OnSelected = box =>
2082 editRagdoll = box.Selected;
2085 SetToggle(limbsToggle,
false);
2086 SetToggle(animsToggle,
false);
2087 SetToggle(jointsToggle,
false);
2088 SetToggle(characterInfoToggle,
false);
2089 paramsToggle.Selected =
true;
2092 ResetParamsEditor();
2095 characterInfoToggle.OnSelected = box =>
2097 editCharacterInfo = box.Selected;
2098 if (editCharacterInfo)
2100 SetToggle(limbsToggle,
false);
2101 SetToggle(animsToggle,
false);
2102 SetToggle(ragdollToggle,
false);
2103 SetToggle(jointsToggle,
false);
2104 paramsToggle.Selected =
true;
2107 ResetParamsEditor();
2110 modesToggle =
new ToggleButton(
new RectTransform(
new Vector2(0.08f, 1), modesPanel.RectTransform,
Anchor.CenterRight,
Pivot.CenterLeft),
Direction.Left);
2111 modesPanel.RectTransform.MinSize =
new Point(0, (
int)(layoutGroup.RectTransform.Children.Sum(c => c.MinSize.Y + layoutGroup.AbsoluteSpacing) * 1.2f));
2114 private void SetToggle(GUITickBox toggle,
bool value)
2116 if (toggle.Selected != value)
2120 toggle.Box.Flash(GUIStyle.Green, useRectangleFlash:
true);
2124 toggle.Box.Flash(GUIStyle.Red, useRectangleFlash:
true);
2127 toggle.Selected = value;
2130 private void CreateButtonsPanel()
2132 buttonsPanel =
new GUIFrame(
new RectTransform(
new Vector2(1.0f, 0.1f), leftArea.RectTransform));
2133 Vector2 buttonSize =
new Vector2(1, 0.45f);
2134 var parent =
new GUIFrame(
new RectTransform(
new Vector2(0.85f, 0.70f), buttonsPanel.RectTransform,
Anchor.Center), style:
null);
2135 var reloadTexturesButton =
new GUIButton(
new RectTransform(buttonSize, parent.RectTransform,
Anchor.TopCenter), GetCharacterEditorTranslation(
"ReloadTextures"));
2136 reloadTexturesButton.OnClicked += (button, userData) =>
2138 foreach (var limb
in character.AnimController.Limbs)
2140 if (limb ==
null) {
continue; }
2141 limb.ActiveSprite?.ReloadTexture();
2142 limb.WearingItems.ForEach(i => i.Sprite.ReloadTexture());
2143 limb.OtherWearables.ForEach(w => w.Sprite.ReloadTexture());
2148 var recreateButton =
new GUIButton(
new RectTransform(buttonSize, parent.RectTransform,
Anchor.BottomCenter), GetCharacterEditorTranslation(
"RecreateRagdoll"))
2150 ToolTip = GetCharacterEditorTranslation(
"RecreateRagdollTooltip"),
2151 OnClicked = (button, data) =>
2154 character.AnimController.ResetLimbs();
2158 GUITextBlock.AutoScaleAndNormalize(reloadTexturesButton.TextBlock, recreateButton.TextBlock);
2159 buttonsPanelToggle =
new ToggleButton(
new RectTransform(
new Vector2(0.08f, 1), buttonsPanel.RectTransform,
Anchor.CenterRight,
Pivot.CenterLeft),
Direction.Left);
2160 buttonsPanel.RectTransform.MinSize =
new Point(0, (
int)(parent.RectTransform.Children.Sum(c => c.MinSize.Y) * 1.5f));
2164 private void CreateOptionsPanel(Vector2 toggleSize)
2166 optionsPanel =
new GUIFrame(
new RectTransform(
new Vector2(1, 0.3f), rightArea.RectTransform));
2167 var layoutGroup =
new GUILayoutGroup(
new RectTransform(innerScale, optionsPanel.RectTransform,
Anchor.Center))
2169 AbsoluteSpacing = 2,
2172 new GUITextBlock(
new RectTransform(
new Vector2(0.03f, 0.0f), layoutGroup.RectTransform), GetCharacterEditorTranslation(
"OptionsPanel"), font: GUIStyle.LargeFont);
2173 freezeToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"Freeze"))
2178 isFrozen = box.Selected;
2182 new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"AutoFreeze"))
2187 autoFreeze = box.Selected;
2191 new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"LimbPairEditing"))
2194 Enabled = character.IsHumanoid,
2197 limbPairEditing = box.Selected;
2201 animTestPoseToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"AnimationTestPose"))
2203 Selected = character.AnimController.AnimationTestPose,
2207 character.AnimController.AnimationTestPose = box.Selected;
2211 new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"AutoMove"))
2213 Selected = character.OverrideMovement !=
null,
2216 character.OverrideMovement = box.Selected ?
new Vector2(1, 0) as Vector2? : null;
2220 new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("FollowCursor"))
2225 character.FollowCursor = box.Selected;
2229 new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"EditBackgroundColor"))
2234 displayBackgroundColor = box.Selected;
2238 optionsToggle =
new ToggleButton(
new RectTransform(
new Vector2(0.08f, 1), optionsPanel.RectTransform,
Anchor.CenterLeft,
Pivot.CenterRight),
Direction.Right);
2239 optionsPanel.RectTransform.MinSize =
new Point(0, (
int)(layoutGroup.RectTransform.Children.Sum(c => c.MinSize.Y + layoutGroup.AbsoluteSpacing) * 1.2f));
2242 private void CreateContextualControls()
2244 Point elementSize =
new Point(120, 20).Multiply(GUI.Scale);
2245 int textAreaHeight = 20;
2247 backgroundColorPanel =
new GUIFrame(
new RectTransform(
new Vector2(0.5f, 0.1f), centerArea.RectTransform,
Anchor.TopRight)
2249 AbsoluteOffset = new Point(10, 0).Multiply(GUI.Scale)
2252 CanBeFocused =
false
2255 var frame =
new GUIFrame(
new RectTransform(
new Point(500, 80).Multiply(GUI.Scale), backgroundColorPanel.RectTransform,
Anchor.TopRight), style:
null, color: Color.Black * 0.4f);
2256 new GUITextBlock(
new RectTransform(
new Vector2(0.2f, 1), frame.RectTransform)
2258 MinSize = new Point(80, 26)
2259 }, GetCharacterEditorTranslation(
"BackgroundColor") +
":", textColor: Color.WhiteSmoke);
2260 var inputArea =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.7f, 1), frame.RectTransform,
Anchor.TopRight)
2262 AbsoluteOffset = new Point(20, 0).Multiply(GUI.Scale)
2263 }, isHorizontal:
true, childAnchor:
Anchor.CenterRight)
2266 RelativeSpacing = 0.01f
2268 var fields =
new GUIComponent[4];
2269 string[] colorComponentLabels = {
"R",
"G",
"B" };
2270 for (
int i = 2; i >= 0; i--)
2272 var element =
new GUIFrame(
new RectTransform(
new Vector2(0.3f, 1), inputArea.RectTransform)
2274 MinSize = new Point(40, 0),
2275 MaxSize = new Point(100, 50)
2276 }, style:
null, color: Color.Black * 0.6f);
2277 var colorLabel =
new GUITextBlock(
new RectTransform(
new Vector2(0.3f, 1), element.RectTransform,
Anchor.CenterLeft), colorComponentLabels[i],
2278 font: GUIStyle.SmallFont, textAlignment: Alignment.CenterLeft);
2279 GUINumberInput numberInput =
new GUINumberInput(
new RectTransform(
new Vector2(0.7f, 1), element.RectTransform,
Anchor.CenterRight),
2280 NumberType.Int, relativeButtonAreaWidth: 0.25f)
2282 Font = GUIStyle.SmallFont
2284 numberInput.MinValueInt = 0;
2285 numberInput.MaxValueInt = 255;
2286 numberInput.Font = GUIStyle.SmallFont;
2290 colorLabel.TextColor = GUIStyle.Red;
2291 numberInput.IntValue = backgroundColor.R;
2292 numberInput.OnValueChanged += (numInput) => backgroundColor.R = (
byte)numInput.IntValue;
2295 colorLabel.TextColor = GUIStyle.Green;
2296 numberInput.IntValue = backgroundColor.G;
2297 numberInput.OnValueChanged += (numInput) => backgroundColor.G = (
byte)numInput.IntValue;
2300 colorLabel.TextColor = Color.DeepSkyBlue;
2301 numberInput.IntValue = backgroundColor.B;
2302 numberInput.OnValueChanged += (numInput) => backgroundColor.B = (
byte)numInput.IntValue;
2307 spriteSheetControls =
new GUIFrame(
new RectTransform(
new Vector2(0.5f, 0.1f), centerArea.RectTransform,
Anchor.BottomLeft)
2309 RelativeOffset = new Vector2(0, 0.1f)
2312 CanBeFocused =
false
2314 var layoutGroupSpriteSheet =
new GUILayoutGroup(
new RectTransform(Vector2.One, spriteSheetControls.RectTransform))
2316 AbsoluteSpacing = 5,
2317 CanBeFocused =
false
2319 new GUITextBlock(
new RectTransform(
new Point(elementSize.X, textAreaHeight), layoutGroupSpriteSheet.RectTransform), GetCharacterEditorTranslation(
"SpriteSheetZoom") +
":", Color.White);
2320 var spriteSheetControlElement =
new GUIFrame(
new RectTransform(
new Point(elementSize.X * 2, textAreaHeight), layoutGroupSpriteSheet.RectTransform), style:
null);
2321 CalculateSpritesheetZoom();
2322 spriteSheetZoomBar =
new GUIScrollBar(
new RectTransform(
new Vector2(0.69f, 1), spriteSheetControlElement.RectTransform,
Anchor.CenterLeft), barSize: 0.2f, style:
"GUISlider")
2324 BarScroll = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(spriteSheetMinZoom, spriteSheetMaxZoom, spriteSheetZoom)),
2326 OnMoved = (scrollBar, value) =>
2328 spriteSheetZoom = MathHelper.Lerp(spriteSheetMinZoom, spriteSheetMaxZoom, value);
2332 new GUIButton(
new RectTransform(
new Vector2(0.3f, 1.25f), spriteSheetControlElement.RectTransform,
Anchor.CenterRight), GetCharacterEditorTranslation(
"Reset"), style:
"GUIButtonFreeScale")
2334 OnClicked = (box, data) =>
2336 spriteSheetZoom = Math.Min(1, spriteSheetMaxZoom);
2337 spriteSheetZoomBar.BarScroll = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(spriteSheetMinZoom, spriteSheetMaxZoom, spriteSheetZoom));
2341 new GUITickBox(
new RectTransform(
new Point(elementSize.X, textAreaHeight), layoutGroupSpriteSheet.RectTransform), GetCharacterEditorTranslation(
"HideBodySprites"))
2343 TextColor = Color.White,
2345 OnSelected = (GUITickBox box) =>
2347 hideBodySheet = box.Selected;
2351 new GUITickBox(
new RectTransform(
new Point(elementSize.X, textAreaHeight), layoutGroupSpriteSheet.RectTransform), GetCharacterEditorTranslation(
"ShowWearables"))
2353 TextColor = Color.White,
2355 OnSelected = (GUITickBox box) =>
2357 displayWearables = box.Selected;
2358 if (displayWearables)
2369 new GUITickBox(
new RectTransform(
new Point(elementSize.X, textAreaHeight), layoutGroupSpriteSheet.RectTransform), GetCharacterEditorTranslation(
"Unrestrict"))
2371 TextColor = Color.White,
2373 OnSelected = (GUITickBox box) =>
2375 SetSpritesheetRestriction(box.Selected);
2379 resetSpriteOrientationButtonParent =
new GUIFrame(
new RectTransform(
new Vector2(0.1f, 0.025f), centerArea.RectTransform,
Anchor.BottomCenter)
2381 AbsoluteOffset = new Point(0, -5).Multiply(GUI.Scale),
2382 RelativeOffset = new Vector2(-0.05f, 0)
2385 CanBeFocused =
false
2387 new GUIButton(
new RectTransform(Vector2.One, resetSpriteOrientationButtonParent.RectTransform,
Anchor.TopRight), GetCharacterEditorTranslation(
"Reset"), style:
"GUIButtonFreeScale")
2389 OnClicked = (box, data) =>
2391 IEnumerable<Limb> limbs = selectedLimbs;
2394 limbs = selectedJoints.Select(j => PlayerInput.KeyDown(Keys.LeftAlt) ? j.LimbB : j.LimbA);
2396 foreach (var limb
in limbs)
2398 TryUpdateSubParam(limb.Params,
"spriteorientation".ToIdentifier(),
float.NaN);
2399 if (limbPairEditing)
2401 UpdateOtherLimbs(limb, l => TryUpdateSubParam(l.Params,
"spriteorientation".ToIdentifier(),
float.NaN));
2408 limbControls =
new GUIFrame(
new RectTransform(Vector2.One, centerArea.RectTransform), style:
null) { CanBeFocused =
false };
2409 var layoutGroupLimbControls =
new GUILayoutGroup(
new RectTransform(Vector2.One, limbControls.RectTransform), childAnchor:
Anchor.TopLeft) { CanBeFocused =
false };
2410 lockSpriteOriginToggle =
new GUITickBox(
new RectTransform(
new Point(elementSize.X, textAreaHeight), layoutGroupLimbControls.RectTransform), GetCharacterEditorTranslation(
"LockSpriteOrigin"))
2412 TextColor = Color.White,
2414 OnSelected = (GUITickBox box) =>
2416 lockSpriteOrigin = box.Selected;
2420 new GUITickBox(
new RectTransform(
new Point(elementSize.X, textAreaHeight), layoutGroupLimbControls.RectTransform), GetCharacterEditorTranslation(
"LockSpritePosition"))
2422 TextColor = Color.White,
2424 OnSelected = (GUITickBox box) =>
2426 lockSpritePosition = box.Selected;
2430 new GUITickBox(
new RectTransform(
new Point(elementSize.X, textAreaHeight), layoutGroupLimbControls.RectTransform), GetCharacterEditorTranslation(
"LockSpriteSize"))
2432 TextColor = Color.White,
2434 OnSelected = (GUITickBox box) =>
2436 lockSpriteSize = box.Selected;
2440 recalculateColliderToggle =
new GUITickBox(
new RectTransform(
new Point(elementSize.X, textAreaHeight), layoutGroupLimbControls.RectTransform), GetCharacterEditorTranslation(
"AdjustCollider"))
2442 TextColor = Color.White,
2444 OnSelected = (GUITickBox box) =>
2446 recalculateCollider = box.Selected;
2447 showCollidersToggle.Selected = recalculateCollider;
2451 new GUITickBox(
new RectTransform(
new Point(elementSize.X, textAreaHeight), layoutGroupLimbControls.RectTransform), GetCharacterEditorTranslation(
"OnlyShowSelectedLimbs"))
2453 TextColor = Color.White,
2454 Selected = onlyShowSourceRectForSelectedLimbs,
2455 OnSelected = (GUITickBox box) =>
2457 onlyShowSourceRectForSelectedLimbs = box.Selected;
2463 Point sliderSize =
new Point(300, 20).Multiply(GUI.Scale);
2464 jointControls =
new GUIFrame(
new RectTransform(
new Vector2(0.5f, 0.075f), centerArea.RectTransform), style:
null) { CanBeFocused =
false };
2465 var layoutGroupJoints =
new GUILayoutGroup(
new RectTransform(Vector2.One, jointControls.RectTransform), childAnchor:
Anchor.TopLeft) { CanBeFocused =
false };
2466 copyJointsToggle =
new GUITickBox(
new RectTransform(
new Point(elementSize.X, textAreaHeight), layoutGroupJoints.RectTransform), GetCharacterEditorTranslation(
"CopyJointSettings"))
2468 ToolTip = GetCharacterEditorTranslation(
"CopyJointSettingsTooltip"),
2470 TextColor = copyJointSettings ? GUIStyle.Red : Color.White,
2471 OnSelected = (GUITickBox box) =>
2473 copyJointSettings = box.Selected;
2474 box.TextColor = copyJointSettings ? GUIStyle.Red : Color.White;
2479 ragdollControls =
new GUIFrame(
new RectTransform(
new Vector2(0.5f, 0.25f), centerArea.RectTransform), style:
null) { CanBeFocused =
false };
2480 var layoutGroupRagdoll =
new GUILayoutGroup(
new RectTransform(Vector2.One, ragdollControls.RectTransform), childAnchor:
Anchor.TopLeft) { CanBeFocused =
false };
2481 var uniformScalingToggle =
new GUITickBox(
new RectTransform(
new Point(elementSize.X, textAreaHeight), layoutGroupRagdoll.RectTransform), GetCharacterEditorTranslation(
"UniformScale"))
2484 OnSelected = (GUITickBox box) =>
2486 uniformScaling = box.Selected;
2490 uniformScalingToggle.TextColor = Color.White;
2491 var jointScaleElement =
new GUIFrame(
new RectTransform(sliderSize +
new Point(0, textAreaHeight), layoutGroupRagdoll.RectTransform), style:
null);
2492 var jointScaleText =
new GUITextBlock(
new RectTransform(
new Point(elementSize.X, textAreaHeight), jointScaleElement.RectTransform), $
"{GetCharacterEditorTranslation("JointScale
")}: {RagdollParams.JointScale.FormatDoubleDecimal()}", Color.WhiteSmoke, textAlignment: Alignment.Center);
2493 var limbScaleElement =
new GUIFrame(
new RectTransform(sliderSize +
new Point(0, textAreaHeight), layoutGroupRagdoll.RectTransform), style:
null);
2494 var limbScaleText =
new GUITextBlock(
new RectTransform(
new Point(elementSize.X, textAreaHeight), limbScaleElement.RectTransform), $
"{GetCharacterEditorTranslation("LimbScale
")}: {RagdollParams.LimbScale.FormatDoubleDecimal()}", Color.WhiteSmoke, textAlignment: Alignment.Center);
2495 jointScaleBar =
new GUIScrollBar(
new RectTransform(sliderSize, jointScaleElement.RectTransform,
Anchor.BottomLeft), barSize: 0.1f, style:
"GUISlider")
2497 BarScroll = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(RagdollParams.MIN_SCALE, RagdollParams.MAX_SCALE, RagdollParams.JointScale)),
2499 OnMoved = (scrollBar, value) =>
2501 float v = MathHelper.Lerp(RagdollParams.MIN_SCALE, RagdollParams.MAX_SCALE, value);
2502 UpdateJointScale(v);
2506 limbScaleBar.BarScroll = value;
2511 limbScaleBar =
new GUIScrollBar(
new RectTransform(sliderSize, limbScaleElement.RectTransform,
Anchor.BottomLeft), barSize: 0.1f, style:
"GUISlider")
2513 BarScroll = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(RagdollParams.MIN_SCALE, RagdollParams.MAX_SCALE, RagdollParams.LimbScale)),
2515 OnMoved = (scrollBar, value) =>
2517 float v = MathHelper.Lerp(RagdollParams.MIN_SCALE, RagdollParams.MAX_SCALE, value);
2521 UpdateJointScale(v);
2522 jointScaleBar.BarScroll = value;
2527 void UpdateJointScale(
float value)
2529 freezeToggle.Selected =
false;
2530 TryUpdateRagdollParam(
"jointscale", value);
2531 jointScaleText.Text = $
"{GetCharacterEditorTranslation("JointScale
")}: {RagdollParams.JointScale.FormatDoubleDecimal()}";
2532 character.AnimController.ResetJoints();
2534 void UpdateLimbScale(
float value)
2536 TryUpdateRagdollParam(
"limbscale", value);
2537 limbScaleText.Text = $
"{GetCharacterEditorTranslation("LimbScale
")}: {RagdollParams.LimbScale.FormatDoubleDecimal()}";
2540 limbScaleBar.Bar.OnClicked += (button, data) =>
2543 RagdollParams.StoreSnapshot();
2546 jointScaleBar.Bar.OnClicked += (button, data) =>
2552 RagdollParams.StoreSnapshot();
2557 Point buttonSize =
new Point(200, 40).Multiply(GUI.Scale);
2558 extraRagdollControls =
new GUIFrame(
new RectTransform(
new Point(buttonSize.X, buttonSize.Y * 4), centerArea.RectTransform,
Anchor.BottomRight)
2560 AbsoluteOffset = new Point(30, 0).Multiply(GUI.Scale),
2561 MinSize = new Point(0, 120)
2562 }, style:
null, color: Color.Black)
2564 CanBeFocused =
false
2566 var paddedFrame =
new GUILayoutGroup(
new RectTransform(Vector2.One * 0.95f, extraRagdollControls.RectTransform,
Anchor.Center))
2571 var buttons = GUI.CreateButtons(4,
new Vector2(1, 0.25f), paddedFrame.RectTransform,
Anchor.TopCenter, style:
"GUIButtonSmallFreeScale");
2572 deleteSelectedButton = buttons[0];
2573 deleteSelectedButton.Text = GetCharacterEditorTranslation(
"DeleteSelected");
2574 deleteSelectedButton.OnClicked = (button, data) =>
2579 duplicateLimbButton = buttons[1];
2580 duplicateLimbButton.Text = GetCharacterEditorTranslation(
"DuplicateLimb");
2581 duplicateLimbButton.OnClicked = (button, data) =>
2583 CopyLimb(selectedLimbs.FirstOrDefault());
2586 createJointButton = buttons[2];
2587 createJointButton.Text = GetCharacterEditorTranslation(
"CreateJoint");
2588 createJointButton.OnClicked = (button, data) =>
2590 ToggleJointCreationMode();
2593 createLimbButton = buttons[3];
2594 createLimbButton.Text = GetCharacterEditorTranslation(
"CreateLimb");
2595 createLimbButton.OnClicked = (button, data) =>
2597 ToggleLimbCreationMode();
2600 GUITextBlock.AutoScaleAndNormalize(buttons.Select(b => b.TextBlock));
2603 animationControls =
new GUIFrame(
new RectTransform(Vector2.One, centerArea.RectTransform), style:
null) { CanBeFocused =
false };
2604 var layoutGroupAnimation =
new GUILayoutGroup(
new RectTransform(Vector2.One, animationControls.RectTransform), childAnchor:
Anchor.TopLeft) { CanBeFocused =
false };
2605 var animationSelectionElement =
new GUIFrame(
new RectTransform(
new Point(elementSize.X * 2 - (
int)(5 * GUI.xScale), elementSize.Y), layoutGroupAnimation.RectTransform), style:
null);
2606 var animationSelectionText =
new GUITextBlock(
new RectTransform(
new Point(elementSize.X, elementSize.Y), animationSelectionElement.RectTransform), GetCharacterEditorTranslation(
"SelectedAnimation"), Color.WhiteSmoke, textAlignment: Alignment.CenterRight);
2607 animSelection =
new GUIDropDown(
new RectTransform(
new Point((
int)(150 * GUI.xScale), elementSize.Y), animationSelectionElement.RectTransform,
Anchor.Center,
Pivot.CenterLeft), elementCount: 5);
2608 if (character.AnimController.CanWalk)
2615 if (character.AnimController.CanWalk && character.IsHumanoid)
2619 if (character.AnimController.ForceSelectAnimationType ==
AnimationType.NotDefined)
2625 animSelection.SelectItem(character.AnimController.ForceSelectAnimationType);
2627 animSelection.OnSelected += (element, data) =>
2629 AnimationType previousAnim = character.AnimController.ForceSelectAnimationType;
2630 character.AnimController.ForceSelectAnimationType = (
AnimationType)data;
2631 switch (character.AnimController.ForceSelectAnimationType)
2636 character.AnimController.forceStanding =
true;
2637 character.ForceRun = character.AnimController.ForceSelectAnimationType ==
AnimationType.Run;
2638 if (!wallCollisionsEnabled)
2640 SetWallCollisions(
true);
2644 TeleportTo(spawnPosition);
2648 character.AnimController.forceStanding =
false;
2649 character.ForceRun =
false;
2650 if (wallCollisionsEnabled)
2652 SetWallCollisions(
false);
2656 character.AnimController.forceStanding =
false;
2657 character.ForceRun =
true;
2658 if (wallCollisionsEnabled)
2660 SetWallCollisions(
false);
2664 throw new NotImplementedException();
2666 ResetParamsEditor();
2671 private void CreateCharacterSelectionPanel()
2673 characterSelectionPanel =
new GUIFrame(
new RectTransform(
new Vector2(1, 0.2f), rightArea.RectTransform));
2674 var content =
new GUILayoutGroup(
new RectTransform(innerScale, characterSelectionPanel.RectTransform,
Anchor.Center))
2679 var characterLabel =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.0f), content.RectTransform), GetCharacterEditorTranslation(
"CharacterPanel"), font: GUIStyle.LargeFont);
2680 var characterDropDown =
new GUIDropDown(
new RectTransform(
new Vector2(1, 0.2f), content.RectTransform)
2682 RelativeOffset = new Vector2(0, 0.2f)
2683 }, elementCount: 8, style:
null);
2684 characterDropDown.ListBox.Color =
new Color(characterDropDown.ListBox.Color.R, characterDropDown.ListBox.Color.G, characterDropDown.ListBox.Color.B,
byte.MaxValue);
2685 foreach (CharacterPrefab prefab
in CharacterPrefab.Prefabs.OrderByDescending(p => p.Identifier))
2687 Identifier speciesName = prefab.Identifier;
2688 if (ShowCreature(prefab))
2690 characterDropDown.AddItem(speciesName.Value.CapitaliseFirstInvariant(), speciesName).SetAsFirstChild();
2692 else if (!CreatureMetrics.Encountered.Contains(speciesName))
2695 var element = characterDropDown.AddItem(TextManager.Get(
"hiddensubmarines"), Identifier.Empty, textColor: Color.Gray * 0.75f);
2696 element.SetAsLastChild();
2697 element.Enabled =
false;
2700 characterDropDown.SelectItem(currentCharacterIdentifier);
2701 characterDropDown.OnSelected = (component, data) =>
2703 Identifier characterIdentifier = (Identifier)data;
2704 if (characterIdentifier.IsEmpty) {
return true; }
2707 SpawnCharacter(characterIdentifier);
2711 HandleSpawnException(characterIdentifier, e);
2715 if (currentCharacterIdentifier == CharacterPrefab.HumanSpeciesName)
2717 var jobDropDown =
new GUIDropDown(
new RectTransform(
new Vector2(1, 0.15f), content.RectTransform)
2719 RelativeOffset = new Vector2(0, 0.45f)
2720 }, elementCount: 8, style:
null);
2721 jobDropDown.ListBox.Color =
new Color(jobDropDown.ListBox.Color.R, jobDropDown.ListBox.Color.G, jobDropDown.ListBox.Color.B,
byte.MaxValue);
2722 jobDropDown.AddItem(
"None");
2723 JobPrefab.Prefabs.Where(j => !j.HiddenJob).ForEach(j => jobDropDown.AddItem(j.Name, j.Identifier));
2724 jobDropDown.SelectItem(selectedJob);
2725 jobDropDown.OnSelected = (component, data) =>
2727 Identifier newJob = data is Identifier jobIdentifier ? jobIdentifier : Identifier.Empty;
2728 if (newJob != selectedJob)
2730 selectedJob = newJob;
2731 SpawnCharacter(currentCharacterIdentifier);
2736 var charButtons =
new GUIFrame(
new RectTransform(
new Vector2(1, 0.25f), parent: content.RectTransform, anchor:
Anchor.BottomLeft), style:
null);
2737 var prevCharacterButton =
new GUIButton(
new RectTransform(
new Vector2(0.5f, 1.0f), charButtons.RectTransform,
Anchor.TopLeft), GetCharacterEditorTranslation(
"PreviousCharacter"));
2738 prevCharacterButton.TextBlock.AutoScaleHorizontal =
true;
2739 prevCharacterButton.OnClicked += (b, obj) =>
2741 Identifier characterIdentifier = GetPreviousCharacterIdentifier();
2744 SpawnCharacter(characterIdentifier);
2748 HandleSpawnException(characterIdentifier, e);
2752 var nextCharacterButton =
new GUIButton(
new RectTransform(
new Vector2(0.5f, 1.0f), charButtons.RectTransform,
Anchor.TopRight), GetCharacterEditorTranslation(
"NextCharacter"));
2753 prevCharacterButton.TextBlock.AutoScaleHorizontal =
true;
2754 nextCharacterButton.OnClicked += (b, obj) =>
2756 Identifier characterIdentifier = GetNextCharacterIdentifier();
2759 SpawnCharacter(characterIdentifier);
2763 HandleSpawnException(characterIdentifier, e);
2767 charButtons.RectTransform.MinSize =
new Point(0, prevCharacterButton.RectTransform.MinSize.Y);
2768 characterPanelToggle =
new ToggleButton(
new RectTransform(
new Vector2(0.08f, 1), characterSelectionPanel.RectTransform,
Anchor.CenterLeft,
Pivot.CenterRight),
Direction.Right);
2769 characterSelectionPanel.RectTransform.MinSize =
new Point(0, (
int)(content.RectTransform.Children.Sum(c => c.MinSize.Y) * 1.2f));
2771 void HandleSpawnException(Identifier characterIdentifier, Exception e)
2773 if (characterIdentifier != CharacterPrefab.HumanSpeciesName)
2775 DebugConsole.ThrowError($
"Failed to spawn the character \"{characterIdentifier}\".", e);
2776 SpawnCharacter(CharacterPrefab.HumanSpeciesName);
2780 throw new Exception($
"Failed to spawn the character \"{characterIdentifier}\".", innerException: e);
2785 private void CreateFileEditPanel()
2787 Vector2 buttonSize =
new Vector2(1, 0.04f);
2789 fileEditPanel =
new GUIFrame(
new RectTransform(
new Vector2(1, 0.4f), rightArea.RectTransform));
2790 var layoutGroup =
new GUILayoutGroup(
new RectTransform(innerScale, fileEditPanel.RectTransform,
Anchor.Center))
2792 AbsoluteSpacing = 1,
2796 new GUITextBlock(
new RectTransform(
new Vector2(0.03f, 0.0f), layoutGroup.RectTransform), GetCharacterEditorTranslation(
"FileEditPanel"), font: GUIStyle.LargeFont);
2799 new GUIFrame(
new RectTransform(buttonSize / 2, layoutGroup.RectTransform), style:
null) { CanBeFocused =
false };
2800 var saveAllButton =
new GUIButton(
new RectTransform(buttonSize, layoutGroup.RectTransform), TextManager.Get(
"editor.saveall"));
2801 saveAllButton.Color = GUIStyle.Green;
2802 saveAllButton.OnClicked += (button, userData) =>
2805 if (VanillaCharacters.Contains(CharacterPrefab.Prefabs[currentCharacterIdentifier].ContentFile))
2807 GUI.AddMessage(GetCharacterEditorTranslation(
"CannotEditVanillaCharacters"), GUIStyle.Red, font: GUIStyle.LargeFont);
2811 ContentPath texturePath = ContentPath.FromRaw(character.Prefab.ContentPackage, RagdollParams.Texture);
2812 if (!character.IsHuman && (texturePath.IsNullOrWhiteSpace() || !File.Exists(texturePath.Value)))
2814 DebugConsole.ThrowError($
"Invalid texture path: {RagdollParams.Texture}");
2819 character.Params.Save();
2820 GUI.AddMessage(GetCharacterEditorTranslation(
"CharacterSavedTo").Replace(
"[path]", CharacterParams.Path.Value), GUIStyle.Green, font: GUIStyle.Font, lifeTime: 5);
2821 character.AnimController.SaveRagdoll();
2822 GUI.AddMessage(GetCharacterEditorTranslation(
"RagdollSavedTo").Replace(
"[path]", RagdollParams.Path.Value), GUIStyle.Green, font: GUIStyle.Font, lifeTime: 5);
2823 AnimParams.ForEach(p => p.Save());
2829 new GUIFrame(
new RectTransform(buttonSize / 2, layoutGroup.RectTransform), style:
null) { CanBeFocused =
false };
2831 Vector2 messageBoxRelSize =
new Vector2(0.5f, 0.7f);
2832 var saveRagdollButton =
new GUIButton(
new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"SaveRagdoll"));
2833 saveRagdollButton.OnClicked += (button, userData) =>
2835 var box =
new GUIMessageBox(GetCharacterEditorTranslation(
"SaveRagdoll"), $
"{GetCharacterEditorTranslation("ProvideFileName
")}: ",
new LocalizedString[] { TextManager.Get(
"Cancel"), TextManager.Get(
"Save") }, messageBoxRelSize);
2836 var inputField =
new GUITextBox(
new RectTransform(
new Point(box.Content.Rect.Width, (
int)(30 * GUI.yScale)), box.Content.RectTransform,
Anchor.Center), RagdollParams.Name.RemoveWhitespace());
2837 box.Buttons[0].OnClicked += (b, d) =>
2842 box.Buttons[1].OnClicked += (b, d) =>
2845 if (VanillaCharacters.Contains(CharacterPrefab.Prefabs[currentCharacterIdentifier].ContentFile))
2847 GUI.AddMessage(GetCharacterEditorTranslation(
"CannotEditVanillaCharacters"), GUIStyle.Red, font: GUIStyle.LargeFont);
2852 character.AnimController.SaveRagdoll(inputField.Text);
2853 GUI.AddMessage(GetCharacterEditorTranslation(
"RagdollSavedTo").Replace(
"[path]", RagdollParams.Path.Value), Color.Green, font: GUIStyle.Font);
2854 RagdollParams.ClearCache();
2855 ResetParamsEditor();
2861 var loadRagdollButton =
new GUIButton(
new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"LoadRagdoll"));
2862 loadRagdollButton.OnClicked += (button, userData) =>
2864 var loadBox =
new GUIMessageBox(GetCharacterEditorTranslation(
"LoadRagdoll"),
"",
new LocalizedString[] { TextManager.Get(
"Cancel"), TextManager.Get(
"Load"), TextManager.Get(
"Delete") }, messageBoxRelSize);
2865 loadBox.Buttons[0].OnClicked += loadBox.Close;
2866 var listBox =
new GUIListBox(
new RectTransform(
new Vector2(0.9f, 0.6f), loadBox.Content.RectTransform,
Anchor.TopCenter))
2868 PlaySoundOnSelect =
true,
2870 var deleteButton = loadBox.Buttons[2];
2871 deleteButton.Enabled =
false;
2872 void PopulateListBox()
2876 var filePaths = Directory.GetFiles(RagdollParams.Folder);
2877 foreach (var path
in filePaths)
2879 GUITextBlock textBlock =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.1f), listBox.Content.RectTransform) { MinSize = new Point(0, 30) },
2880 ToolBox.LimitString(Path.GetFileNameWithoutExtension(path), GUIStyle.Font, listBox.Rect.Width - 80))
2889 DebugConsole.ThrowErrorLocalized(GetCharacterEditorTranslation(
"CouldntOpenDirectory").Replace(
"[folder]", RagdollParams.Folder), e);
2894 string selectedFile =
null;
2895 listBox.OnSelected += (component, data) =>
2897 selectedFile = data as string;
2899 var fileName = Path.GetFileNameWithoutExtension(selectedFile);
2900 deleteButton.Enabled = fileName != RagdollParams.Name && fileName != RagdollParams.GetDefaultFileName(character.SpeciesName);
2903 deleteButton.OnClicked += (btn, data) =>
2905 if (selectedFile ==
null)
2910 var msgBox =
new GUIMessageBox(
2911 TextManager.Get(
"DeleteDialogLabel"),
2912 TextManager.GetWithVariable(
"DeleteDialogQuestion",
"[file]", selectedFile),
2913 new LocalizedString[] { TextManager.Get(
"Yes"), TextManager.Get(
"Cancel") });
2914 msgBox.Buttons[0].OnClicked += (b, d) =>
2918 File.Delete(selectedFile);
2919 GUI.AddMessage(GetCharacterEditorTranslation(
"RagdollDeletedFrom").Replace(
"[file]", selectedFile), GUIStyle.Red, font: GUIStyle.Font);
2923 DebugConsole.ThrowErrorLocalized(TextManager.Get(
"DeleteFileError").Replace(
"[file]", selectedFile), e);
2926 listBox.ClearChildren();
2928 selectedFile =
null;
2931 msgBox.Buttons[1].OnClicked += (b, d) =>
2938 loadBox.Buttons[1].OnClicked += (btn, data) =>
2940 string fileName = Path.GetFileNameWithoutExtension(selectedFile);
2941 Identifier baseSpecies = character.GetBaseCharacterSpeciesName();
2942 var ragdoll = character.IsHumanoid
2943 ? RagdollParams.GetRagdollParams<HumanRagdollParams>(character.SpeciesName, baseSpecies, fileName, character.Prefab.ContentPackage) as RagdollParams
2944 : RagdollParams.GetRagdollParams<FishRagdollParams>(character.SpeciesName, baseSpecies, fileName, character.Prefab.ContentPackage);
2945 ragdoll.Reset(
true);
2946 GUI.AddMessage(GetCharacterEditorTranslation(
"RagdollLoadedFrom").Replace(
"[file]", selectedFile), Color.WhiteSmoke, font: GUIStyle.Font);
2947 RecreateRagdoll(ragdoll);
2948 CreateContextualControls();
2954 var saveAnimationButton =
new GUIButton(
new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"SaveAnimation"));
2955 saveAnimationButton.OnClicked += (button, userData) =>
2957 var box =
new GUIMessageBox(GetCharacterEditorTranslation(
"SaveAnimation"),
string.Empty,
new LocalizedString[] { TextManager.Get(
"Cancel"), TextManager.Get(
"Save") }, messageBoxRelSize);
2958 var textArea =
new GUIFrame(
new RectTransform(
new Vector2(1, 0.1f), box.Content.RectTransform) { MinSize = new Point(350, 30) }, style:
null);
2959 var inputLabel =
new GUITextBlock(
new RectTransform(
new Vector2(0.3f, 1), textArea.RectTransform,
Anchor.CenterLeft) { MinSize = new Point(250, 30) }, $
"{GetCharacterEditorTranslation("ProvideFileName
")}: ");
2960 var inputField =
new GUITextBox(
new RectTransform(
new Vector2(0.45f, 1), textArea.RectTransform,
Anchor.CenterRight) { MinSize = new Point(100, 30) }, CurrentAnimation.Name);
2962 var typeSelectionArea =
new GUIFrame(
new RectTransform(
new Vector2(1f, 0.1f), box.Content.RectTransform) { MinSize = new Point(0, 30) }, style:
null);
2963 var typeLabel =
new GUITextBlock(
new RectTransform(
new Vector2(0.45f, 1), typeSelectionArea.RectTransform,
Anchor.CenterLeft), $
"{GetCharacterEditorTranslation("SelectAnimationType
")}: ");
2964 var typeDropdown =
new GUIDropDown(
new RectTransform(
new Vector2(0.45f, 1), typeSelectionArea.RectTransform,
Anchor.CenterRight), elementCount: 4);
2965 foreach (
object enumValue
in Enum.GetValues(typeof(
AnimationType)))
2969 typeDropdown.AddItem(enumValue.ToString(), enumValue);
2972 AnimationType selectedType = character.AnimController.ForceSelectAnimationType;
2973 typeDropdown.OnSelected = (component, data) =>
2976 inputField.Text = character.AnimController.GetAnimationParamsFromType(selectedType)?.Name.RemoveWhitespace();
2979 typeDropdown.SelectItem(selectedType);
2980 box.Buttons[0].OnClicked += (b, d) =>
2985 box.Buttons[1].OnClicked += (b, d) =>
2988 if (VanillaCharacters.Contains(CharacterPrefab.Prefabs[currentCharacterIdentifier].ContentFile))
2990 GUI.AddMessage(GetCharacterEditorTranslation(
"CannotEditVanillaCharacters"), GUIStyle.Red, font: GUIStyle.LargeFont);
2995 var animParams = character.AnimController.GetAnimationParamsFromType(selectedType);
2996 if (animParams ==
null) {
return true; }
2997 string fileName = inputField.Text;
2998 animParams.Save(fileName);
2999 string newPath = animParams.Path.ToString();
3000 GUI.AddMessage(GetCharacterEditorTranslation(
"AnimationOfTypeSavedTo").Replace(
"[type]", selectedType.ToString()).Replace(
"[path]", newPath), Color.Green, font: GUIStyle.Font);
3001 AnimationParams.ClearCache();
3002 ResetParamsEditor();
3008 var loadAnimationButton =
new GUIButton(
new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"LoadAnimation"));
3009 loadAnimationButton.OnClicked += (button, userData) =>
3011 var loadBox =
new GUIMessageBox(GetCharacterEditorTranslation(
"LoadAnimation"),
"",
new LocalizedString[] { TextManager.Get(
"Cancel"), TextManager.Get(
"Load"), TextManager.Get(
"Delete") }, messageBoxRelSize);
3012 loadBox.Buttons[0].OnClicked += loadBox.Close;
3013 var listBox =
new GUIListBox(
new RectTransform(
new Vector2(0.9f, 0.6f), loadBox.Content.RectTransform))
3015 PlaySoundOnSelect =
true,
3017 var deleteButton = loadBox.Buttons[2];
3018 deleteButton.Enabled =
false;
3020 var typeSelectionArea =
new GUIFrame(
new RectTransform(
new Vector2(0.9f, 0.1f), loadBox.Content.RectTransform) { MinSize = new Point(0, 30) }, style:
null);
3021 var typeLabel =
new GUITextBlock(
new RectTransform(
new Vector2(0.45f, 1), typeSelectionArea.RectTransform,
Anchor.CenterLeft), $
"{GetCharacterEditorTranslation("SelectAnimationType
")}: ");
3022 var typeDropdown =
new GUIDropDown(
new RectTransform(
new Vector2(0.45f, 1), typeSelectionArea.RectTransform,
Anchor.CenterRight), elementCount: 4);
3023 foreach (
object enumValue
in Enum.GetValues(typeof(
AnimationType)))
3027 typeDropdown.AddItem(enumValue.ToString(), enumValue);
3030 AnimationType selectedType = character.AnimController.ForceSelectAnimationType;
3031 typeDropdown.OnSelected = (component, data) =>
3037 typeDropdown.SelectItem(selectedType);
3038 void PopulateListBox()
3042 listBox.ClearChildren();
3043 var filePaths = Directory.GetFiles(CurrentAnimation.Folder);
3044 foreach (var path
in AnimationParams.FilterAndSortFiles(filePaths, selectedType))
3046 GUITextBlock textBlock =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.1f), listBox.Content.RectTransform) { MinSize = new Point(0, 30) }, ToolBox.LimitString(Path.GetFileNameWithoutExtension(path), GUIStyle.Font, listBox.Rect.Width - 80))
3055 DebugConsole.ThrowErrorLocalized(GetCharacterEditorTranslation(
"CouldntOpenDirectory").Replace(
"[folder]", CurrentAnimation.Folder), e);
3060 string selectedFile =
null;
3061 listBox.OnSelected += (component, data) =>
3063 selectedFile = data as string;
3065 string fileName = Path.GetFileNameWithoutExtension(selectedFile);
3066 deleteButton.Enabled = fileName != CurrentAnimation.Name && fileName != AnimationParams.GetDefaultFileName(character.SpeciesName, CurrentAnimation.AnimationType);
3069 deleteButton.OnClicked += (btn, data) =>
3071 if (selectedFile ==
null)
3076 var msgBox =
new GUIMessageBox(
3077 TextManager.Get(
"DeleteDialogLabel"),
3078 TextManager.GetWithVariable(
"DeleteDialogQuestion",
"[file]", selectedFile),
3079 new LocalizedString[] { TextManager.Get(
"Yes"), TextManager.Get(
"Cancel") });
3080 msgBox.Buttons[0].OnClicked += (b, d) =>
3084 File.Delete(selectedFile);
3085 GUI.AddMessage(GetCharacterEditorTranslation(
"AnimationOfTypeDeleted").Replace(
"[type]", selectedType.ToString()).Replace(
"[file]", selectedFile), GUIStyle.Red, font: GUIStyle.Font);
3089 DebugConsole.ThrowErrorLocalized(TextManager.GetWithVariable(
"DeleteFileError",
"[file]", selectedFile), e);
3093 selectedFile =
null;
3096 msgBox.Buttons[1].OnClicked += (b, d) =>
3103 loadBox.Buttons[1].OnClicked += (btn, data) =>
3105 if (character.AnimController.TryLoadAnimation(selectedType, Path.GetFileNameWithoutExtension(selectedFile), out AnimationParams animationParams, throwErrors:
true))
3107 animationParams.Reset(forceReload:
true);
3108 GUI.AddMessage(GetCharacterEditorTranslation(
"AnimationOfTypeLoaded").Replace(
"[type]", selectedType.ToString()).Replace(
"[file]", animationParams.FileNameWithoutExtension), Color.WhiteSmoke, font: GUIStyle.Font);
3110 ResetParamsEditor();
3118 new GUIFrame(
new RectTransform(buttonSize / 2, layoutGroup.RectTransform), style:
null) { CanBeFocused =
false };
3119 var resetButton =
new GUIButton(
new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"ResetButton"));
3120 resetButton.Color = GUIStyle.Red;
3121 resetButton.OnClicked += (button, userData) =>
3123 CharacterParams.Reset(
true);
3124 AnimParams.ForEach(p => p.Reset(
true));
3125 character.AnimController.ResetRagdoll();
3127 jointCreationMode = JointCreationMode.None;
3128 isDrawingLimb =
false;
3130 jointStartLimb =
null;
3136 new GUIFrame(
new RectTransform(buttonSize / 2, layoutGroup.RectTransform), style:
null) { CanBeFocused =
false };
3137 new GUIButton(
new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"CreateNewCharacter"))
3139 OnClicked = (button, data) =>
3142 Wizard.Instance.SelectTab(Wizard.Tab.Character);
3146 new GUIButton(
new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"CopyCharacter"))
3148 ToolTip = GetCharacterEditorTranslation(
"CopyCharacterToolTip"),
3149 OnClicked = (button, data) =>
3152 PrepareCharacterCopy();
3153 Wizard.Instance.SelectTab(Wizard.Tab.Character);
3158 GUITextBlock.AutoScaleAndNormalize(layoutGroup.Children.Where(c => c is GUIButton).Select(c => ((GUIButton)c).TextBlock));
3160 fileEditToggle =
new ToggleButton(
new RectTransform(
new Vector2(0.08f, 1), fileEditPanel.RectTransform,
Anchor.CenterLeft,
Pivot.CenterRight),
Direction.Right);
3164 characterInfoToggle.Selected =
false;
3165 ragdollToggle.Selected =
false;
3166 limbsToggle.Selected =
false;
3167 animsToggle.Selected =
false;
3168 spritesheetToggle.Selected =
false;
3169 jointsToggle.Selected =
false;
3170 paramsToggle.Selected =
false;
3171 skeletonToggle.Selected =
false;
3172 damageModifiersToggle.Selected =
false;
3175 fileEditPanel.RectTransform.MinSize =
new Point(0, (
int)(layoutGroup.RectTransform.Children.Sum(c => c.MinSize.Y + layoutGroup.AbsoluteSpacing) * 1.2f));
3183 AnimParams.ForEach(a => a.Serialize());
3187 #region ToggleButtons
3194 private class ToggleButton
3199 public float OpenState {
get;
private set; } = 1;
3201 private bool isHidden;
3202 public bool IsHidden
3204 get {
return isHidden; }
3208 RefreshToggleButtonState();
3212 public ToggleButton(RectTransform rectT,
Direction dir)
3214 toggleButton =
new GUIButton(rectT, style:
"UIToggleButton")
3216 OnClicked = (button, data) =>
3218 IsHidden = !IsHidden;
3223 RefreshToggleButtonState();
3226 public void RefreshToggleButtonState()
3228 foreach (GUIComponent child
in toggleButton.Children)
3233 child.SpriteEffects = isHidden ? SpriteEffects.None : SpriteEffects.FlipHorizontally;
3236 child.SpriteEffects = isHidden ? SpriteEffects.FlipHorizontally : SpriteEffects.None;
3242 public void UpdateOpenState(
float deltaTime, Vector2 hiddenPos, RectTransform panel)
3244 panel.AbsoluteOffset =
new Vector2(MathHelper.SmoothStep(hiddenPos.X, 0.0f, OpenState), panel.AbsoluteOffset.Y).ToPoint();
3245 OpenState = isHidden ? Math.Max(OpenState - deltaTime * 5, 0) : Math.Min(OpenState + deltaTime * 5, 1);
3252 private CharacterParams CharacterParams => character.Params;
3253 private List<AnimationParams> AnimParams => character.AnimController.AllAnimParams;
3254 private AnimationParams CurrentAnimation => character.AnimController.CurrentAnimationParams;
3255 private RagdollParams RagdollParams => character.AnimController.RagdollParams;
3257 private void ResetParamsEditor()
3259 ParamsEditor.Instance.Clear();
3260 if (!editRagdoll && !editCharacterInfo && !editJoints && !editLimbs && !editAnimations)
3262 paramsToggle.Selected =
false;
3265 if (editCharacterInfo)
3267 var mainEditor = ParamsEditor.Instance;
3268 CharacterParams.AddToEditor(mainEditor, space: 10);
3269 var characterEditor = CharacterParams.SerializableEntityEditor;
3271 characterEditor.AddCustomContent(
new GUIFrame(
new RectTransform(
new Point(characterEditor.Rect.Width, (
int)(10 * GUI.yScale)), characterEditor.RectTransform), style:
null) { CanBeFocused = false }, 1);
3272 if (CharacterParams.AI !=
null)
3274 CreateAddButton(CharacterParams.AI.SerializableEntityEditor, () => CharacterParams.AI.TryAddEmptyTarget(out _), GetCharacterEditorTranslation(
"AddAITarget"));
3275 foreach (var target
in CharacterParams.AI.Targets)
3277 CreateCloseButton(target.SerializableEntityEditor, () => CharacterParams.AI.RemoveTarget(target), size: 0.8f);
3280 foreach (var emitter
in CharacterParams.BloodEmitters)
3282 CreateCloseButton(emitter.SerializableEntityEditor, () => CharacterParams.RemoveBloodEmitter(emitter));
3284 foreach (var emitter
in CharacterParams.GibEmitters)
3286 CreateCloseButton(emitter.SerializableEntityEditor, () => CharacterParams.RemoveGibEmitter(emitter));
3288 foreach (var emitter
in CharacterParams.DamageEmitters)
3290 CreateCloseButton(emitter.SerializableEntityEditor, () => CharacterParams.RemoveDamageEmitter(emitter));
3292 foreach (var sound
in CharacterParams.Sounds)
3294 CreateCloseButton(sound.SerializableEntityEditor, () => CharacterParams.RemoveSound(sound));
3296 foreach (var inventory
in CharacterParams.Inventories)
3298 var editor = inventory.SerializableEntityEditor;
3299 CreateCloseButton(editor, () => CharacterParams.RemoveInventory(inventory));
3300 foreach (var item
in inventory.Items)
3302 CreateCloseButton(item.SerializableEntityEditor, () => inventory.RemoveItem(item), size: 0.8f);
3304 CreateAddButton(editor, () => inventory.AddItem(), GetCharacterEditorTranslation(
"AddInventoryItem"));
3306 CreateAddButtonAtLast(mainEditor, () => CharacterParams.AddBloodEmitter(), GetCharacterEditorTranslation(
"AddBloodEmitter"));
3307 CreateAddButtonAtLast(mainEditor, () => CharacterParams.AddGibEmitter(), GetCharacterEditorTranslation(
"AddGibEmitter"));
3308 CreateAddButtonAtLast(mainEditor, () => CharacterParams.AddDamageEmitter(), GetCharacterEditorTranslation(
"AddDamageEmitter"));
3309 CreateAddButtonAtLast(mainEditor, () => CharacterParams.AddSound(), GetCharacterEditorTranslation(
"AddSound"));
3310 CreateAddButtonAtLast(mainEditor, () => CharacterParams.AddInventory(), GetCharacterEditorTranslation(
"AddInventory"));
3312 else if (editAnimations)
3314 character.AnimController.CurrentAnimationParams?.AddToEditor(ParamsEditor.Instance, space: 10);
3320 RagdollParams.AddToEditor(ParamsEditor.Instance, alsoChildren:
false, space: 10);
3321 RagdollParams.Colliders.ForEach(c => c.AddToEditor(ParamsEditor.Instance,
false, 10));
3323 else if (editJoints)
3325 if (selectedJoints.Any())
3327 selectedJoints.ForEach(j => j.Params.AddToEditor(ParamsEditor.Instance,
true, space: 10));
3331 RagdollParams.Joints.ForEach(jp => jp.AddToEditor(ParamsEditor.Instance,
false, space: 10));
3336 if (selectedLimbs.Any())
3338 foreach (var limb
in selectedLimbs)
3340 var mainEditor = ParamsEditor.Instance;
3341 var limbEditor = limb.Params.SerializableEntityEditor;
3342 limb.Params.AddToEditor(mainEditor,
true, space: 0);
3343 foreach (var damageModifier
in limb.Params.DamageModifiers)
3345 CreateCloseButton(damageModifier.SerializableEntityEditor, () => limb.Params.RemoveDamageModifier(damageModifier));
3347 if (limb.Params.Sound ==
null)
3349 CreateAddButtonAtLast(mainEditor, () => limb.Params.AddSound(), GetCharacterEditorTranslation(
"AddSound"));
3353 CreateCloseButton(limb.Params.Sound.SerializableEntityEditor, () => limb.Params.RemoveSound());
3355 if (limb.Params.LightSource ==
null)
3357 CreateAddButtonAtLast(mainEditor, () => limb.Params.AddLight(), GetCharacterEditorTranslation(
"AddLightSource"));
3361 CreateCloseButton(limb.Params.LightSource.SerializableEntityEditor, () => limb.Params.RemoveLight());
3363 if (limb.Params.Attack ==
null)
3365 CreateAddButtonAtLast(mainEditor, () => limb.Params.AddAttack(), GetCharacterEditorTranslation(
"AddAttack"));
3369 var attackParams = limb.Params.Attack;
3370 foreach (var affliction
in attackParams.Attack.Afflictions)
3372 if (attackParams.AfflictionEditors.TryGetValue(affliction.Key, out SerializableEntityEditor afflictionEditor))
3374 CreateCloseButton(afflictionEditor, () => attackParams.RemoveAffliction(affliction.Value), size: 0.8f);
3377 var attackEditor = attackParams.SerializableEntityEditor;
3378 CreateAddButton(attackEditor, () => attackParams.AddNewAffliction(), GetCharacterEditorTranslation(
"AddAffliction"));
3379 CreateCloseButton(attackEditor, () => limb.Params.RemoveAttack());
3380 var space =
new GUIFrame(
new RectTransform(
new Point(attackEditor.RectTransform.Rect.Width, (
int)(20 * GUI.yScale)), attackEditor.RectTransform), style:
null, color: ParamsEditor.Color)
3382 CanBeFocused =
false
3384 attackEditor.AddCustomContent(space, attackEditor.ContentCount);
3386 CreateAddButtonAtLast(mainEditor, () => limb.Params.AddDamageModifier(), GetCharacterEditorTranslation(
"AddDamageModifier"));
3391 character.AnimController.Limbs.ForEach(l => l.Params.AddToEditor(ParamsEditor.Instance,
false, space: 10));
3396 void CreateCloseButton(SerializableEntityEditor editor, Action onButtonClicked,
float size = 1)
3398 if (editor ==
null) {
return; }
3400 var parent =
new GUIFrame(
new RectTransform(
new Point(editor.Rect.Width, (
int)(height * size * GUI.yScale)), editor.RectTransform, isFixedSize:
true), style:
null)
3402 CanBeFocused =
false
3404 new GUIButton(
new RectTransform(
new Vector2(0.9f), parent.RectTransform,
Anchor.BottomRight, scaleBasis:
ScaleBasis.BothHeight), style:
"GUICancelButton", color: GUIStyle.Red)
3406 OnClicked = (button, data) =>
3409 ResetParamsEditor();
3413 editor.AddCustomContent(parent, 0);
3416 void CreateAddButtonAtLast(ParamsEditor editor, Action onButtonClicked, LocalizedString text)
3418 if (editor ==
null) {
return; }
3419 var parentFrame =
new GUIFrame(
new RectTransform(
new Point(editor.EditorBox.Rect.Width, (
int)(50 * GUI.yScale)), editor.EditorBox.Content.RectTransform), style:
null, color: ParamsEditor.Color)
3421 CanBeFocused =
false
3423 new GUIButton(
new RectTransform(
new Vector2(0.45f, 0.6f), parentFrame.RectTransform,
Anchor.Center), text)
3425 OnClicked = (button, data) =>
3428 ResetParamsEditor();
3434 void CreateAddButton(SerializableEntityEditor editor, Action onButtonClicked, LocalizedString text)
3436 if (editor ==
null) {
return; }
3437 var parent =
new GUIFrame(
new RectTransform(
new Point(editor.Rect.Width, (
int)(60 * GUI.yScale)), editor.RectTransform), style:
null)
3439 CanBeFocused =
false
3441 new GUIButton(
new RectTransform(
new Vector2(0.45f, 0.4f), parent.RectTransform,
Anchor.CenterLeft), text)
3443 OnClicked = (button, data) =>
3446 ResetParamsEditor();
3450 editor.AddCustomContent(parent, editor.ContentCount);
3454 private void TryUpdateAnimParam(
string name,
object value) => TryUpdateAnimParam(name.ToIdentifier(), value);
3455 private void TryUpdateAnimParam(Identifier name,
object value) => TryUpdateParam(character.AnimController.CurrentAnimationParams, name, value);
3456 private void TryUpdateRagdollParam(
string name,
object value) => TryUpdateRagdollParam(name.ToIdentifier(), value);
3457 private void TryUpdateRagdollParam(Identifier name,
object value) => TryUpdateParam(RagdollParams, name, value);
3459 private void TryUpdateParam(EditableParams editableParams, Identifier name,
object value)
3461 if (editableParams.SerializableEntityEditor ==
null)
3463 editableParams.AddToEditor(ParamsEditor.Instance);
3465 if (editableParams.SerializableProperties.TryGetValue(name, out SerializableProperty p))
3467 editableParams.SerializableEntityEditor.UpdateValue(p, value);
3471 private void TryUpdateJointParam(LimbJoint joint,
string name,
object value) => TryUpdateJointParam(joint, name.ToIdentifier(), value);
3472 private void TryUpdateJointParam(LimbJoint joint, Identifier name,
object value) => TryUpdateSubParam(joint.Params, name, value);
3473 private void TryUpdateLimbParam(Limb limb,
string name,
object value) => TryUpdateLimbParam(limb, name.ToIdentifier(), value);
3474 private void TryUpdateLimbParam(Limb limb, Identifier name,
object value) => TryUpdateSubParam(limb.Params, name, value);
3476 private void TryUpdateSubParam(RagdollParams.SubParam ragdollSubParams, Identifier name,
object value)
3478 if (ragdollSubParams.SerializableEntityEditor ==
null)
3480 ragdollSubParams.AddToEditor(ParamsEditor.Instance);
3482 if (ragdollSubParams.SerializableProperties.TryGetValue(name, out SerializableProperty p))
3484 ragdollSubParams.SerializableEntityEditor.UpdateValue(p, value);
3488 var subParams = ragdollSubParams.SubParams.Where(sp => sp.SerializableProperties.ContainsKey(name)).FirstOrDefault();
3489 if (subParams !=
null)
3491 if (subParams.SerializableProperties.TryGetValue(name, out p))
3493 if (subParams.SerializableEntityEditor ==
null)
3495 subParams.AddToEditor(ParamsEditor.Instance);
3497 subParams.SerializableEntityEditor.UpdateValue(p, value);
3502 DebugConsole.ThrowErrorLocalized(GetCharacterEditorTranslation(
"NoFieldForParameterFound").Replace(
"[parameter]", name.Value));
3509 private Vector2 ScreenToSim(
float x,
float y) => ScreenToSim(
new Vector2(x, y));
3510 private Vector2 ScreenToSim(Vector2 p) => ConvertUnits.ToSimUnits(Cam.ScreenToWorld(p)) +
Submarine.MainSub.SimPosition;
3511 private Vector2 SimToScreen(
float x,
float y) => SimToScreen(
new Vector2(x, y));
3512 private Vector2 SimToScreen(Vector2 p) => Cam.WorldToScreen(ConvertUnits.ToDisplayUnits(p +
Submarine.MainSub.SimPosition));
3514 private bool IsMatchingLimb(Limb limb1, Limb limb2, LimbJoint joint1, LimbJoint joint2) =>
3515 joint1.BodyA == limb1.body.FarseerBody && joint2.BodyA == limb2.body.FarseerBody ||
3516 joint1.BodyB == limb1.body.FarseerBody && joint2.BodyB == limb2.body.FarseerBody;
3518 private void ValidateJoint(LimbJoint limbJoint)
3520 if (limbJoint.UpperLimit < limbJoint.LowerLimit)
3522 if (limbJoint.LowerLimit > 0.0f)
3524 limbJoint.LowerLimit -= MathHelper.TwoPi;
3526 if (limbJoint.UpperLimit < 0.0f)
3528 limbJoint.UpperLimit += MathHelper.TwoPi;
3531 limbJoint.LowerLimit = MathUtils.WrapAnglePi(limbJoint.LowerLimit);
3532 limbJoint.UpperLimit = MathUtils.WrapAnglePi(limbJoint.UpperLimit);
3535 private Limb GetClosestLimbOnRagdoll(Vector2 targetPos, Func<Limb, bool> filter =
null)
3537 Limb closestLimb =
null;
3538 float closestDistance =
float.MaxValue;
3539 foreach (Limb l
in character.AnimController.Limbs)
3541 if (filter ==
null ?
true : filter(l))
3543 float distance = Vector2.DistanceSquared(SimToScreen(l.SimPosition), targetPos);
3544 if (distance < closestDistance)
3547 closestDistance = distance;
3554 private Limb GetClosestLimbOnSpritesheet(Vector2 targetPos, Func<Limb, bool> filter =
null)
3556 Limb closestLimb =
null;
3557 float closestDistance =
float.MaxValue;
3558 foreach (Limb l
in character.AnimController.Limbs)
3560 if (l ==
null) {
continue; }
3561 if (filter ==
null ?
true : filter(l))
3563 float distance = Vector2.DistanceSquared(GetLimbSpritesheetRect(l).
Center.ToVector2(), targetPos);
3564 if (distance < closestDistance)
3567 closestDistance = distance;
3574 private Rectangle GetLimbSpritesheetRect(Limb limb)
3576 int offsetX = spriteSheetOffsetX;
3577 int offsetY = spriteSheetOffsetY;
3579 if (Textures !=
null)
3581 for (
int i = 0; i < Textures.Count; i++)
3583 if (limb.ActiveSprite.FilePath != texturePaths[i])
3585 offsetY += (int)(Textures[i].Height * spriteSheetZoom);
3589 rect = limb.ActiveSprite.SourceRect;
3590 rect.Size = rect.MultiplySize(spriteSheetZoom);
3591 rect.Location = rect.Location.Multiply(spriteSheetZoom);
3601 private void UpdateSourceRect(Limb limb, Rectangle newRect,
bool resize)
3603 Sprite activeSprite = limb.ActiveSprite;
3604 activeSprite.SourceRect = newRect;
3605 if (limb.DamagedSprite !=
null)
3607 limb.DamagedSprite.SourceRect = activeSprite.SourceRect;
3609 Vector2 colliderSize =
new Vector2(ConvertUnits.ToSimUnits(newRect.Width), ConvertUnits.ToSimUnits(newRect.Height));
3612 if (recalculateCollider)
3614 RecalculateCollider(limb, colliderSize);
3617 var spritePos =
new Vector2(spriteSheetOffsetX, GetOffsetY(activeSprite));
3618 var originWidget = GetLimbEditWidget($
"{limb.Params.ID}_origin", limb);
3619 if (!resize && originWidget !=
null)
3621 Vector2 newOrigin = (originWidget.DrawPos - spritePos - activeSprite.SourceRect.Location.ToVector2() * spriteSheetZoom) / spriteSheetZoom;
3622 RecalculateOrigin(limb, newOrigin);
3626 RecalculateOrigin(limb);
3628 TryUpdateLimbParam(limb,
"sourcerect", newRect);
3629 if (limbPairEditing)
3631 UpdateOtherLimbs(limb, otherLimb =>
3633 otherLimb.ActiveSprite.SourceRect = newRect;
3634 if (otherLimb.DamagedSprite !=
null)
3636 otherLimb.DamagedSprite.SourceRect = newRect;
3640 if (recalculateCollider)
3642 RecalculateCollider(otherLimb, colliderSize);
3645 if (!resize && originWidget !=
null)
3647 Vector2 newOrigin = (originWidget.DrawPos - spritePos - activeSprite.SourceRect.Location.ToVector2() * spriteSheetZoom) / spriteSheetZoom;
3648 RecalculateOrigin(otherLimb, newOrigin);
3652 RecalculateOrigin(otherLimb);
3654 TryUpdateLimbParam(otherLimb,
"sourcerect", newRect);
3659 private void CalculateSpritesheetZoom()
3661 var texture = textures.OrderByDescending(t => t.Width).FirstOrDefault();
3662 if (texture ==
null)
3664 spriteSheetZoom = 1;
3667 float width = texture.Width;
3668 float height = textures.Sum(t => t.Height);
3670 if (unrestrictSpritesheet)
3672 spriteSheetMaxZoom = (GameMain.GraphicsWidth - spriteSheetOffsetX * 2 - margin - leftArea.Rect.Width) / width;
3678 spriteSheetMaxZoom = (centerArea.Rect.Bottom - spriteSheetOffsetY - margin) / height;
3682 spriteSheetMaxZoom = (centerArea.Rect.Left - spriteSheetOffsetX - margin) / width;
3685 spriteSheetMinZoom = spriteSheetMinZoom > spriteSheetMaxZoom ? spriteSheetMaxZoom : 0.25f;
3686 spriteSheetZoom = MathHelper.Clamp(1, spriteSheetMinZoom, spriteSheetMaxZoom);
3689 private void HandleLimbSelection(Limb limb)
3693 SetToggle(limbsToggle,
true);
3695 if (!selectedLimbs.Contains(limb))
3697 if (!Widget.EnableMultiSelect)
3699 selectedLimbs.Clear();
3701 selectedLimbs.Add(limb);
3702 ResetParamsEditor();
3705 else if (Widget.EnableMultiSelect)
3707 selectedLimbs.Remove(limb);
3708 ResetParamsEditor();
3712 private void OpenDoors()
3714 foreach (var item
in Item.ItemList)
3716 foreach (var component
in item.Components)
3718 if (component is Items.Components.Door door)
3726 private void SaveSnapshot()
3728 if (editJoints || editLimbs || editIK)
3730 RagdollParams.StoreSnapshot();
3734 CurrentAnimation.StoreSnapshot();
3738 private void ToggleJointCreationMode()
3740 switch (jointCreationMode)
3742 case JointCreationMode.None:
3743 jointCreationMode = JointCreationMode.Select;
3744 SetToggle(spritesheetToggle,
true);
3746 case JointCreationMode.Select:
3747 case JointCreationMode.Create:
3748 jointCreationMode = JointCreationMode.None;
3753 private void ToggleLimbCreationMode()
3755 isDrawingLimb = !isDrawingLimb;
3758 SetToggle(spritesheetToggle,
true);
3763 #region Animation Controls
3764 private void DrawAnimationControls(SpriteBatch spriteBatch,
float deltaTime)
3766 var collider = character.AnimController.Collider;
3767 var colliderDrawPos = cam.
WorldToScreen(collider.DrawPosition);
3768 var animParams = character.AnimController.CurrentAnimationParams;
3769 var groundedParams = animParams as GroundedMovementParams;
3770 var humanParams = animParams as IHumanAnimation;
3771 var humanGroundedParams = animParams as HumanGroundedParams;
3772 var humanSwimParams = animParams as HumanSwimParams;
3773 var fishParams = animParams as IFishAnimation;
3774 var fishGroundedParams = animParams as FishGroundedParams;
3775 var fishSwimParams = animParams as FishSwimParams;
3776 var head = character.AnimController.GetLimb(
LimbType.Head);
3777 var torso = character.AnimController.GetLimb(
LimbType.Torso);
3778 var tail = character.AnimController.GetLimb(
LimbType.Tail);
3779 var legs = character.AnimController.GetLimb(
LimbType.Legs);
3780 var thigh = character.AnimController.GetLimb(
LimbType.RightThigh) ?? character.AnimController.GetLimb(
LimbType.LeftThigh);
3781 var foot = character.AnimController.GetLimb(
LimbType.RightFoot) ?? character.AnimController.GetLimb(
LimbType.LeftFoot);
3782 var hand = character.AnimController.GetLimb(
LimbType.RightHand) ?? character.AnimController.GetLimb(
LimbType.LeftHand);
3783 var arm = character.AnimController.GetLimb(
LimbType.RightArm) ?? character.AnimController.GetLimb(
LimbType.LeftArm);
3785 float dir = character.AnimController.Dir;
3786 Vector2 GetSimSpaceForward() => animParams.IsSwimAnimation ? Vector2.Transform(Vector2.UnitY, Matrix.CreateRotationZ(collider.Rotation)) : Vector2.UnitX * character.AnimController.Dir;
3787 Vector2 GetScreenSpaceForward() => animParams.IsSwimAnimation ? VectorExtensions.BackwardFlipped(collider.Rotation, 1) : Vector2.UnitX * character.AnimController.Dir;
3788 bool ShowCycleWidget() => PlayerInput.KeyDown(Keys.LeftAlt) && (CurrentAnimation is IHumanAnimation || CurrentAnimation is GroundedMovementParams);
3789 if (!PlayerInput.KeyDown(Keys.LeftAlt) && (animParams is IHumanAnimation || animParams is GroundedMovementParams))
3791 GUI.DrawString(spriteBatch,
new Vector2(GameMain.GraphicsWidth / 2 - 120, 150), GetCharacterEditorTranslation(
"HoldLeftAltToAdjustCycleSpeed"), Color.White, Color.Black * 0.5f, 10, GUIStyle.Font);
3794 Vector2 referencePoint = cam.
WorldToScreen(head !=
null ? head.DrawPosition: collider.DrawPosition);
3795 Vector2 drawPos = referencePoint;
3796 if (ShowCycleWidget())
3798 GetAnimationWidget(
"CycleSpeed", Color.MediumPurple, Color.Black, size: 20, sizeMultiplier: 1.5f, shape:
WidgetShape.Circle, initMethod: w =>
3800 float multiplier = 0.5f;
3801 w.Tooltip = GetCharacterEditorTranslation(
"CycleSpeed");
3804 var refPoint = cam.WorldToScreen(head != null ? head.DrawPosition : collider.DrawPosition);
3805 w.DrawPos = refPoint + GetScreenSpaceForward() * ConvertUnits.ToDisplayUnits(CurrentAnimation.CycleSpeed * multiplier) * Cam.Zoom;
3807 w.Tooltip = $
"{GetCharacterEditorTranslation("CycleSpeed
")}: {CurrentAnimation.CycleSpeed.FormatDoubleDecimal()}";
3809 w.MouseHeld += dTime =>
3814 float speed = CurrentAnimation.CycleSpeed + ConvertUnits.ToSimUnits(Vector2.Multiply(PlayerInput.MouseSpeed / multiplier, GetScreenSpaceForward()).Combine()) / Cam.Zoom;
3815 TryUpdateAnimParam(
"cyclespeed", speed);
3816 w.Tooltip = $
"{GetCharacterEditorTranslation("CycleSpeed
")}: {CurrentAnimation.CycleSpeed.FormatDoubleDecimal()}";
3819 w.PreUpdate += dTime =>
3821 if (!ShowCycleWidget())
3827 w.PreDraw += (sp, dTime) =>
3834 w.PostDraw += (sp, dTime) =>
3838 GUI.DrawLine(spriteBatch, w.DrawPos, cam.
WorldToScreen(head !=
null ? head.DrawPosition : collider.DrawPosition), Color.MediumPurple);
3841 }).Draw(spriteBatch, deltaTime);
3845 GetAnimationWidget(
"MovementSpeed", Color.Turquoise, Color.Black, size: 20, sizeMultiplier: 1.5f, shape:
WidgetShape.Circle, initMethod: w =>
3847 float multiplier = 0.5f;
3848 w.Tooltip = GetCharacterEditorTranslation(
"MovementSpeed");
3851 var refPoint = cam.WorldToScreen(head != null ? head.DrawPosition : collider.DrawPosition);
3852 w.DrawPos = refPoint + GetScreenSpaceForward() * ConvertUnits.ToDisplayUnits(CurrentAnimation.MovementSpeed * multiplier) * Cam.Zoom;
3854 w.MouseHeld += dTime =>
3859 float speed = CurrentAnimation.MovementSpeed + ConvertUnits.ToSimUnits(Vector2.Multiply(PlayerInput.MouseSpeed / multiplier, GetScreenSpaceForward()).Combine()) / Cam.Zoom;
3860 TryUpdateAnimParam(
"movementspeed", MathHelper.Clamp(speed, 0.1f, Ragdoll.MAX_SPEED));
3862 if (humanSwimParams != null)
3864 TryUpdateAnimParam(
"cyclespeed", character.AnimController.CurrentAnimationParams.MovementSpeed);
3866 w.Tooltip = $
"{GetCharacterEditorTranslation("MovementSpeed
")}: {CurrentAnimation.MovementSpeed.FormatSingleDecimal()}";
3869 w.PreUpdate += dTime =>
3871 if (ShowCycleWidget())
3877 w.PreDraw += (sp, dTime) =>
3884 w.PostDraw += (sp, dTime) =>
3888 GUI.DrawLine(spriteBatch, w.DrawPos, Cam.WorldToScreen(head !=
null ? head.DrawPosition : collider.DrawPosition), Color.Turquoise);
3891 }).Draw(spriteBatch, deltaTime);
3897 DrawRadialWidget(spriteBatch, Cam.WorldToScreen(head.DrawPosition), animParams.HeadAngle, GetCharacterEditorTranslation(
"HeadAngle"), Color.White,
3898 angle => TryUpdateAnimParam(
"headangle", angle), circleRadius: 25, rotationOffset: -collider.Rotation + head.Params.GetSpriteOrientation() * dir, clockWise: dir < 0, wrapAnglePi:
true, holdPosition:
true);
3900 Color color = GUIStyle.Red;
3901 if (animParams.IsGroundedAnimation)
3903 if (humanGroundedParams !=
null && character.AnimController is HumanoidAnimController humanAnimController)
3905 GetAnimationWidget(
"HeadPosition", color, Color.Black, initMethod: w =>
3907 w.Tooltip = GetCharacterEditorTranslation(
"Head");
3908 w.Refresh = () => w.DrawPos = Cam.WorldToScreen(
3910 head.DrawPosition.X + ConvertUnits.ToDisplayUnits(humanAnimController.HeadLeanAmount * character.AnimController.Dir),
3911 ConvertUnits.ToDisplayUnits(head.PullJointWorldAnchorB.Y)));
3912 bool isHorizontal = false;
3913 bool isDirectionSet = false;
3914 w.MouseDown += () => isDirectionSet = false;
3915 w.MouseHeld += dTime =>
3917 if (PlayerInput.MouseSpeed.NearlyEquals(Vector2.Zero)) { return; }
3918 if (!isDirectionSet)
3920 isHorizontal = Math.Abs(PlayerInput.MouseSpeed.X) > Math.Abs(PlayerInput.MouseSpeed.Y);
3921 isDirectionSet = true;
3923 var scaledInput = ConvertUnits.ToSimUnits(PlayerInput.MouseSpeed) / Cam.Zoom;
3924 if (PlayerInput.KeyDown(Keys.LeftAlt))
3928 TryUpdateAnimParam(
"headleanamount", humanGroundedParams.HeadLeanAmount + scaledInput.X * character.AnimController.Dir);
3930 w.DrawPos = new Vector2(PlayerInput.MousePosition.X, w.DrawPos.Y);
3934 TryUpdateAnimParam(
"headposition", humanGroundedParams.HeadPosition - scaledInput.Y / RagdollParams.JointScale);
3936 w.DrawPos = new Vector2(w.DrawPos.X, PlayerInput.MousePosition.Y);
3941 TryUpdateAnimParam(
"headleanamount", humanGroundedParams.HeadLeanAmount + scaledInput.X * character.AnimController.Dir);
3943 w.DrawPos = new Vector2(PlayerInput.MousePosition.X, w.DrawPos.Y);
3944 TryUpdateAnimParam(
"headposition", humanGroundedParams.HeadPosition - scaledInput.Y / RagdollParams.JointScale);
3946 w.DrawPos = new Vector2(w.DrawPos.X, PlayerInput.MousePosition.Y);
3949 w.PostDraw += (sB, dTime) =>
3951 if (w.IsControlled && isDirectionSet)
3953 if (PlayerInput.KeyDown(Keys.LeftAlt))
3957 GUI.DrawLine(spriteBatch, new Vector2(0, w.DrawPos.Y), new Vector2(GameMain.GraphicsWidth, w.DrawPos.Y), color);
3961 GUI.DrawLine(spriteBatch, new Vector2(w.DrawPos.X, 0), new Vector2(w.DrawPos.X, GameMain.GraphicsHeight), color);
3966 GUI.DrawLine(spriteBatch, new Vector2(0, w.DrawPos.Y), new Vector2(GameMain.GraphicsWidth, w.DrawPos.Y), color);
3967 GUI.DrawLine(spriteBatch, new Vector2(w.DrawPos.X, 0), new Vector2(w.DrawPos.X, GameMain.GraphicsHeight), color);
3970 else if (w.IsSelected)
3972 GUI.DrawLine(spriteBatch, w.DrawPos, cam.WorldToScreen(head.DrawPosition), color);
3975 }).Draw(spriteBatch, deltaTime);
3977 else if (groundedParams !=
null)
3979 GetAnimationWidget(
"HeadPosition", color, Color.Black, initMethod: w =>
3981 w.Tooltip = GetCharacterEditorTranslation(
"HeadPosition");
3982 w.Refresh = () => w.DrawPos = cam.WorldToScreen(new Vector2(head.DrawPosition.X, ConvertUnits.ToDisplayUnits(head.PullJointWorldAnchorB.Y)));
3983 w.MouseHeld += dTime =>
3985 w.DrawPos = cam.WorldToScreen(new Vector2(head.DrawPosition.X, ConvertUnits.ToDisplayUnits(head.PullJointWorldAnchorB.Y)));
3986 var scaledInput = ConvertUnits.ToSimUnits(PlayerInput.MouseSpeed) / Cam.Zoom / RagdollParams.JointScale;
3987 TryUpdateAnimParam(
"headposition", groundedParams.HeadPosition - scaledInput.Y);
3989 w.PostDraw += (sB, dTime) =>
3993 GUI.DrawLine(spriteBatch, new Vector2(w.DrawPos.X, 0), new Vector2(w.DrawPos.X, GameMain.GraphicsHeight), color);
3996 }).Draw(spriteBatch, deltaTime);
4002 referencePoint = torso.DrawPosition;
4003 if (animParams is HumanGroundedParams || animParams is HumanSwimParams)
4005 var f = Vector2.Transform(Vector2.UnitY, Matrix.CreateRotationZ(collider.Rotation));
4006 referencePoint -= f * 25f;
4009 DrawRadialWidget(spriteBatch, cam.
WorldToScreen(referencePoint), animParams.TorsoAngle, GetCharacterEditorTranslation(
"TorsoAngle"), Color.White,
4010 angle => TryUpdateAnimParam(
"torsoangle", angle), rotationOffset: -collider.Rotation + torso.Params.GetSpriteOrientation() * dir, clockWise: dir < 0, wrapAnglePi:
true, holdPosition:
true);
4011 Color color = Color.DodgerBlue;
4012 if (animParams.IsGroundedAnimation)
4015 if (humanGroundedParams !=
null && character.AnimController is HumanoidAnimController humanAnimController)
4017 GetAnimationWidget(
"TorsoPosition", color, Color.Black, initMethod: w =>
4019 w.Tooltip = GetCharacterEditorTranslation(
"Torso");
4020 w.Refresh = () => w.DrawPos = cam.WorldToScreen(
4021 new Vector2(torso.DrawPosition.X + ConvertUnits.ToDisplayUnits(humanAnimController.TorsoLeanAmount * character.AnimController.Dir),
4022 ConvertUnits.ToDisplayUnits(torso.PullJointWorldAnchorB.Y)));
4023 bool isHorizontal = false;
4024 bool isDirectionSet = false;
4025 w.MouseDown += () => isDirectionSet = false;
4026 w.MouseHeld += dTime =>
4028 if (PlayerInput.MouseSpeed.NearlyEquals(Vector2.Zero)) { return; }
4029 if (!isDirectionSet)
4031 isHorizontal = Math.Abs(PlayerInput.MouseSpeed.X) > Math.Abs(PlayerInput.MouseSpeed.Y);
4032 isDirectionSet = true;
4034 var scaledInput = ConvertUnits.ToSimUnits(PlayerInput.MouseSpeed) / Cam.Zoom;
4035 if (PlayerInput.KeyDown(Keys.LeftAlt))
4039 TryUpdateAnimParam(
"torsoleanamount", humanGroundedParams.TorsoLeanAmount + scaledInput.X * character.AnimController.Dir);
4041 w.DrawPos = new Vector2(PlayerInput.MousePosition.X, w.DrawPos.Y);
4045 TryUpdateAnimParam(
"torsoposition", humanGroundedParams.TorsoPosition - scaledInput.Y / RagdollParams.JointScale);
4047 w.DrawPos = new Vector2(w.DrawPos.X, PlayerInput.MousePosition.Y);
4052 TryUpdateAnimParam(
"torsoleanamount", humanGroundedParams.TorsoLeanAmount + scaledInput.X * character.AnimController.Dir);
4054 w.DrawPos = new Vector2(PlayerInput.MousePosition.X, w.DrawPos.Y);
4055 TryUpdateAnimParam(
"torsoposition", humanGroundedParams.TorsoPosition - scaledInput.Y / RagdollParams.JointScale);
4057 w.DrawPos = new Vector2(w.DrawPos.X, PlayerInput.MousePosition.Y);
4060 w.PostDraw += (sB, dTime) =>
4062 if (w.IsControlled && isDirectionSet)
4064 if (PlayerInput.KeyDown(Keys.LeftAlt))
4068 GUI.DrawLine(spriteBatch, new Vector2(0, w.DrawPos.Y), new Vector2(GameMain.GraphicsWidth, w.DrawPos.Y), color);
4072 GUI.DrawLine(spriteBatch, new Vector2(w.DrawPos.X, 0), new Vector2(w.DrawPos.X, GameMain.GraphicsHeight), color);
4077 GUI.DrawLine(spriteBatch, new Vector2(0, w.DrawPos.Y), new Vector2(GameMain.GraphicsWidth, w.DrawPos.Y), color);
4078 GUI.DrawLine(spriteBatch, new Vector2(w.DrawPos.X, 0), new Vector2(w.DrawPos.X, GameMain.GraphicsHeight), color);
4081 else if (w.IsSelected)
4083 GUI.DrawLine(spriteBatch, w.DrawPos, cam.WorldToScreen(torso.DrawPosition), color);
4086 }).Draw(spriteBatch, deltaTime);
4088 else if (groundedParams !=
null)
4090 GetAnimationWidget(
"TorsoPosition", color, Color.Black, initMethod: w =>
4092 w.Tooltip = GetCharacterEditorTranslation(
"TorsoPosition");
4093 w.Refresh = () => w.DrawPos = SimToScreen(torso.SimPosition.X, torso.PullJointWorldAnchorB.Y);
4094 w.MouseHeld += dTime =>
4096 w.DrawPos = SimToScreen(torso.SimPosition.X, torso.PullJointWorldAnchorB.Y);
4097 var scaledInput = ConvertUnits.ToSimUnits(PlayerInput.MouseSpeed) / Cam.Zoom / RagdollParams.JointScale;
4098 TryUpdateAnimParam(
"torsoposition", groundedParams.TorsoPosition - scaledInput.Y);
4100 w.PostDraw += (sB, dTime) =>
4104 GUI.DrawLine(spriteBatch, new Vector2(w.DrawPos.X, 0), new Vector2(w.DrawPos.X, GameMain.GraphicsHeight), color);
4107 }).Draw(spriteBatch, deltaTime);
4112 if (tail !=
null && fishParams !=
null)
4114 DrawRadialWidget(spriteBatch, cam.
WorldToScreen(tail.DrawPosition), fishParams.TailAngle, GetCharacterEditorTranslation(
"TailAngle"), Color.White,
4115 angle => TryUpdateAnimParam(
"tailangle", angle), circleRadius: 25, rotationOffset: -collider.Rotation + tail.Params.GetSpriteOrientation() * dir, clockWise: dir < 0, wrapAnglePi:
true, holdPosition:
true);
4120 if (fishParams !=
null)
4122 Vector2 colliderBottom = character.AnimController.GetColliderBottom();
4123 foreach (Limb limb
in character.AnimController.Limbs)
4125 if (limb.type !=
LimbType.LeftFoot && limb.type !=
LimbType.RightFoot)
continue;
4127 if (!fishParams.FootAnglesInRadians.ContainsKey(limb.Params.ID))
4129 fishParams.FootAnglesInRadians[limb.Params.ID] = 0.0f;
4132 DrawRadialWidget(spriteBatch,
4133 cam.
WorldToScreen(
new Vector2(limb.DrawPosition.X, ConvertUnits.ToDisplayUnits(colliderBottom.Y))),
4134 MathHelper.ToDegrees(fishParams.FootAnglesInRadians[limb.Params.ID]),
4135 GetCharacterEditorTranslation(
"FootAngle"), Color.White,
4138 fishParams.FootAnglesInRadians[limb.Params.ID] = MathHelper.ToRadians(angle);
4139 TryUpdateAnimParam(
"footangles", fishParams.FootAngles);
4141 circleRadius: 25, rotationOffset: -collider.Rotation + limb.Params.GetSpriteOrientation() * dir, clockWise: dir < 0, wrapAnglePi:
true, autoFreeze:
true);
4144 else if (humanParams !=
null)
4146 DrawRadialWidget(spriteBatch, cam.
WorldToScreen(foot.DrawPosition), humanParams.FootAngle, GetCharacterEditorTranslation(
"FootAngle"), Color.White,
4147 angle => TryUpdateAnimParam(
"footangle", angle), circleRadius: 25, rotationOffset: -collider.Rotation + foot.Params.GetSpriteOrientation() * dir, clockWise: dir > 0, wrapAnglePi:
true);
4150 if (groundedParams !=
null)
4152 GetAnimationWidget(
"StepSize", Color.LimeGreen, Color.Black, initMethod: w =>
4154 w.Tooltip = GetCharacterEditorTranslation(
"StepSize");
4157 var refPoint = cam.WorldToScreen(new Vector2(
4158 character.AnimController.Collider.DrawPosition.X,
4159 character.AnimController.GetColliderBottom().Y));
4160 var stepSize = ConvertUnits.ToDisplayUnits(character.AnimController.StepSize.Value);
4161 w.DrawPos = refPoint + new Vector2(stepSize.X * character.AnimController.Dir, -stepSize.Y) * Cam.Zoom;
4163 w.MouseHeld += dTime =>
4165 w.DrawPos = PlayerInput.MousePosition;
4166 var transformedInput = ConvertUnits.ToSimUnits(new Vector2(PlayerInput.MouseSpeed.X * character.AnimController.Dir, -PlayerInput.MouseSpeed.Y)) / Cam.Zoom / RagdollParams.JointScale;
4167 TryUpdateAnimParam(
"stepsize", groundedParams.StepSize + transformedInput);
4168 w.Tooltip = $
"{GetCharacterEditorTranslation("StepSize
")}: {groundedParams.StepSize.FormatDoubleDecimal()}";
4170 w.PostDraw += (sp, dTime) =>
4174 GUI.DrawLine(sp, w.DrawPos, SimToScreen(character.AnimController.GetColliderBottom()), Color.LimeGreen);
4177 }).Draw(spriteBatch, deltaTime);
4181 if (humanGroundedParams !=
null)
4183 if (hand !=
null || arm !=
null)
4185 GetAnimationWidget(
"HandMoveAmount", GUIStyle.Green, Color.Black, initMethod: w =>
4187 w.Tooltip = GetCharacterEditorTranslation(
"HandMoveAmount");
4191 var refPoint = cam.WorldToScreen(character.AnimController.Collider.DrawPosition + GetSimSpaceForward() * offset);
4192 var handMovement = ConvertUnits.ToDisplayUnits(humanGroundedParams.HandMoveAmount);
4193 w.DrawPos = refPoint + new Vector2(handMovement.X * character.AnimController.Dir, handMovement.Y) * Cam.Zoom;
4195 w.MouseHeld += dTime =>
4197 w.DrawPos = PlayerInput.MousePosition;
4198 var transformedInput = ConvertUnits.ToSimUnits(new Vector2(PlayerInput.MouseSpeed.X * character.AnimController.Dir, PlayerInput.MouseSpeed.Y) / Cam.Zoom);
4199 TryUpdateAnimParam(
"handmoveamount", humanGroundedParams.HandMoveAmount + transformedInput);
4200 w.Tooltip = $
"{GetCharacterEditorTranslation("HandMoveAmount
")}: {humanGroundedParams.HandMoveAmount.FormatDoubleDecimal()}";
4202 w.PostDraw += (sp, dTime) =>
4206 GUI.DrawLine(sp, w.DrawPos, cam.WorldToScreen(character.AnimController.Collider.DrawPosition + GetSimSpaceForward() * offset), GUIStyle.Green);
4209 }).Draw(spriteBatch, deltaTime);
4213 else if (tail !=
null && fishSwimParams !=
null)
4215 float amplitudeMultiplier = 20;
4216 float lengthMultiplier = 20;
4218 float GetAmplitude() => ConvertUnits.ToDisplayUnits(fishSwimParams.WaveAmplitude) * Cam.Zoom / amplitudeMultiplier;
4219 float GetWaveLength() => ConvertUnits.ToDisplayUnits(fishSwimParams.WaveLength) * Cam.Zoom / lengthMultiplier;
4220 Vector2 GetRefPoint() => cam.
WorldToScreen(collider.DrawPosition) - GetScreenSpaceForward() * ConvertUnits.ToDisplayUnits(collider.Radius) * 3 * Cam.Zoom;
4221 Vector2 GetDrawPos() => GetRefPoint() - GetScreenSpaceForward() * GetWaveLength();
4222 Vector2 GetDir() => GetRefPoint() - GetDrawPos();
4223 Vector2 GetStartPoint() => GetDrawPos() + GetDir() / 2;
4224 Vector2 GetControlPoint() => GetStartPoint() + GetScreenSpaceForward().Right() * character.AnimController.Dir * GetAmplitude();
4225 var lengthWidget = GetAnimationWidget(
"WaveLength", Color.NavajoWhite, Color.Black, size: 15, shape:
WidgetShape.Circle, initMethod: w =>
4227 w.Tooltip = GetCharacterEditorTranslation(
"TailMovementSpeed");
4228 w.Refresh = () => w.DrawPos = GetDrawPos();
4229 w.MouseHeld += dTime =>
4231 float input = Vector2.Multiply(ConvertUnits.ToSimUnits(PlayerInput.MouseSpeed), GetScreenSpaceForward()).Combine() / Cam.Zoom * lengthMultiplier;
4232 TryUpdateAnimParam(
"wavelength", MathHelper.Clamp(fishSwimParams.WaveLength - input, 0, 200));
4235 w.PreDraw += (sp, dTime) =>
4243 var amplitudeWidget = GetAnimationWidget(
"WaveAmplitude", Color.NavajoWhite, Color.Black, size: 15, shape:
WidgetShape.Circle, initMethod: w =>
4245 w.Tooltip = GetCharacterEditorTranslation(
"TailMovementAmount");
4246 w.Refresh = () => w.DrawPos = GetControlPoint();
4247 w.MouseHeld += dTime =>
4249 float input = Vector2.Multiply(ConvertUnits.ToSimUnits(PlayerInput.MouseSpeed), GetScreenSpaceForward().Right()).Combine() * character.AnimController.Dir / Cam.Zoom * amplitudeMultiplier;
4250 TryUpdateAnimParam(
"waveamplitude", MathHelper.Clamp(fishSwimParams.WaveAmplitude + input, -100, 100));
4253 w.PreDraw += (sp, dTime) =>
4261 if (lengthWidget.IsControlled || amplitudeWidget.IsControlled)
4263 GUI.DrawSineWithDots(spriteBatch, GetRefPoint(), -GetDir(), GetAmplitude(), GetWaveLength(), 5000, points, Color.NavajoWhite);
4265 lengthWidget.Draw(spriteBatch, deltaTime);
4266 amplitudeWidget.Draw(spriteBatch, deltaTime);
4269 else if (humanSwimParams !=
null)
4272 float amplitudeMultiplier = 5;
4273 float lengthMultiplier = 5;
4275 float GetAmplitude() => ConvertUnits.ToDisplayUnits(humanSwimParams.LegMoveAmount) * Cam.Zoom / amplitudeMultiplier;
4276 float GetWaveLength() => ConvertUnits.ToDisplayUnits(humanSwimParams.LegCycleLength) * Cam.Zoom / lengthMultiplier;
4277 Vector2 GetRefPoint() => cam.
WorldToScreen(character.DrawPosition - GetScreenSpaceForward().FlipY() * 75);
4278 Vector2 GetDrawPos() => GetRefPoint() - GetScreenSpaceForward() * GetWaveLength();
4279 Vector2 GetDir() => GetRefPoint() - GetDrawPos();
4280 Vector2 GetStartPoint() => GetDrawPos() + GetDir() / 2;
4281 Vector2 GetControlPoint() => GetStartPoint() + GetScreenSpaceForward().Right() * character.AnimController.Dir * GetAmplitude();
4282 var lengthWidget = GetAnimationWidget(
"LegMovementSpeed", Color.NavajoWhite, Color.Black, size: 15, shape:
WidgetShape.Circle, initMethod: w =>
4284 w.Tooltip = GetCharacterEditorTranslation(
"LegMovementSpeed");
4285 w.Refresh = () => w.DrawPos = GetDrawPos();
4286 w.MouseHeld += dTime =>
4288 float input = Vector2.Multiply(ConvertUnits.ToSimUnits(PlayerInput.MouseSpeed), GetScreenSpaceForward()).Combine() / Cam.Zoom * lengthMultiplier;
4289 TryUpdateAnimParam(
"legcyclelength", MathHelper.Clamp(humanSwimParams.LegCycleLength - input, 0, 20));
4292 w.PreDraw += (sp, dTime) =>
4300 var amplitudeWidget = GetAnimationWidget(
"LegMovementAmount", Color.NavajoWhite, Color.Black, size: 15, shape:
WidgetShape.Circle, initMethod: w =>
4302 w.Tooltip = GetCharacterEditorTranslation(
"LegMovementAmount");
4303 w.Refresh = () => w.DrawPos = GetControlPoint();
4304 w.MouseHeld += dTime =>
4306 float input = Vector2.Multiply(ConvertUnits.ToSimUnits(PlayerInput.MouseSpeed), GetScreenSpaceForward().Right()).Combine() * character.AnimController.Dir / Cam.Zoom * amplitudeMultiplier;
4307 TryUpdateAnimParam(
"legmoveamount", MathHelper.Clamp(humanSwimParams.LegMoveAmount + input, -2, 2));
4310 w.PreDraw += (sp, dTime) =>
4318 if (lengthWidget.IsControlled || amplitudeWidget.IsControlled)
4320 GUI.DrawSineWithDots(spriteBatch, GetRefPoint(), -GetDir(), GetAmplitude(), GetWaveLength(), 5000, points, Color.NavajoWhite);
4322 lengthWidget.Draw(spriteBatch, deltaTime);
4323 amplitudeWidget.Draw(spriteBatch, deltaTime);
4325 GetAnimationWidget(
"HandMoveAmount", GUIStyle.Green, Color.Black, initMethod: w =>
4327 w.Tooltip = GetCharacterEditorTranslation(
"HandMoveAmount");
4331 var refPoint = cam.WorldToScreen(collider.DrawPosition + GetSimSpaceForward() * offset);
4332 var handMovement = ConvertUnits.ToDisplayUnits(humanSwimParams.HandMoveAmount);
4333 w.DrawPos = refPoint + new Vector2(handMovement.X * character.AnimController.Dir, handMovement.Y) * Cam.Zoom;
4335 w.MouseHeld += dTime =>
4337 w.DrawPos = PlayerInput.MousePosition;
4338 Vector2 transformedInput = ConvertUnits.ToSimUnits(new Vector2(PlayerInput.MouseSpeed.X * character.AnimController.Dir, PlayerInput.MouseSpeed.Y)) / Cam.Zoom;
4339 Vector2 handMovement = humanSwimParams.HandMoveAmount + transformedInput;
4340 TryUpdateAnimParam(
"handmoveamount", handMovement);
4341 TryUpdateAnimParam(
"handcyclespeed", handMovement.X * 4);
4342 w.Tooltip = $
"{GetCharacterEditorTranslation("HandMoveAmount
")}: {humanSwimParams.HandMoveAmount.FormatDoubleDecimal()}";
4344 w.PostDraw += (sp, dTime) =>
4348 GUI.DrawLine(sp, w.DrawPos, cam.WorldToScreen(collider.DrawPosition + GetSimSpaceForward() * offset), GUIStyle.Green);
4351 }).Draw(spriteBatch, deltaTime);
4354 foreach (Limb limb
in character.AnimController.Limbs)
4358 GUI.DrawRectangle(spriteBatch, SimToScreen(limb.DebugRefPos) - Vector2.One * 3, Vector2.One * 6, Color.White, isFilled:
true);
4359 GUI.DrawRectangle(spriteBatch, SimToScreen(limb.DebugTargetPos) - Vector2.One * 3, Vector2.One * 6, GUIStyle.Green, isFilled:
true);
4366 private Vector2[] corners =
new Vector2[4];
4367 private Vector2[] GetLimbPhysicRect(Limb limb)
4369 Vector2 size = ConvertUnits.ToDisplayUnits(limb.body.GetSize()) * Cam.Zoom;
4370 Vector2 up = VectorExtensions.BackwardFlipped(limb.Rotation);
4371 Vector2 limbScreenPos = cam.
WorldToScreen(limb.DrawPosition);
4372 corners = MathUtils.GetImaginaryRect(corners, up, limbScreenPos, size);
4376 private void DrawLimbEditor(SpriteBatch spriteBatch)
4378 float inputMultiplier = 0.5f;
4379 foreach (Limb limb
in character.AnimController.Limbs)
4381 if (limb ==
null || limb.ActiveSprite ==
null) {
continue; }
4382 var origin = limb.ActiveSprite.Origin;
4383 var sourceRect = limb.ActiveSprite.SourceRect;
4384 Vector2 limbScreenPos = cam.
WorldToScreen(limb.DrawPosition);
4385 bool isSelected = selectedLimbs.Contains(limb);
4386 corners = GetLimbPhysicRect(limb);
4387 if (isSelected && jointStartLimb != limb && jointEndLimb != limb)
4389 GUI.DrawRectangle(spriteBatch, corners, Color.Yellow, thickness: 3);
4391 if (GUI.MouseOn ==
null && Widget.SelectedWidgets.None() && !spriteSheetRect.Contains(PlayerInput.MousePosition) && MathUtils.RectangleContainsPoint(corners, PlayerInput.MousePosition))
4396 if (!lockSpriteOrigin && PlayerInput.PrimaryMouseButtonHeld())
4398 Vector2 forward = Vector2.Transform(Vector2.UnitY, Matrix.CreateRotationZ(limb.Rotation));
4399 var input = -scaledMouseSpeed * inputMultiplier / Cam.Zoom / limb.Scale / limb.TextureScale;
4400 var sprite = limb.ActiveSprite;
4401 origin += input.TransformVector(forward);
4402 var max =
new Vector2(sourceRect.Width, sourceRect.Height);
4403 sprite.Origin = origin.Clamp(Vector2.Zero, max);
4404 if (limb.DamagedSprite !=
null)
4406 limb.DamagedSprite.Origin = sprite.Origin;
4408 if (character.AnimController.IsFlipped)
4410 origin.X = Math.Abs(origin.X - sourceRect.Width);
4412 TryUpdateLimbParam(limb,
"origin", limb.ActiveSprite.RelativeOrigin);
4413 if (limbPairEditing)
4415 UpdateOtherLimbs(limb, otherLimb =>
4417 otherLimb.ActiveSprite.Origin = sprite.Origin;
4418 if (otherLimb.DamagedSprite !=
null)
4420 otherLimb.DamagedSprite.Origin = sprite.Origin;
4422 TryUpdateLimbParam(otherLimb,
"origin", otherLimb.ActiveSprite.RelativeOrigin);
4425 GUI.DrawString(spriteBatch, limbScreenPos +
new Vector2(10, -10), limb.ActiveSprite.RelativeOrigin.FormatDoubleDecimal(), Color.Yellow, Color.Black * 0.5f);
4430 GUI.DrawRectangle(spriteBatch, corners, Color.White);
4431 GUI.DrawString(spriteBatch, limbScreenPos +
new Vector2(10, -10), limb.Name, Color.White, Color.Black * 0.5f);
4437 private void DrawRagdoll(SpriteBatch spriteBatch,
float deltaTime)
4439 bool altDown = PlayerInput.KeyDown(Keys.LeftAlt);
4441 if (!altDown && editJoints && selectedJoints.Any() && jointCreationMode == JointCreationMode.None)
4443 GUI.DrawString(spriteBatch,
new Vector2(GameMain.GraphicsWidth / 2 - 180, 100), GetCharacterEditorTranslation(
"HoldLeftAltToManipulateJoint"), Color.White, Color.Black * 0.5f, 10, GUIStyle.Font);
4446 foreach (Limb limb
in character.AnimController.Limbs)
4452 var pullJointWidgetSize =
new Vector2(5, 5);
4453 Vector2 tformedPullPos = SimToScreen(limb.PullJointWorldAnchorA) + limb.body.DrawPositionOffset;
4454 GUI.DrawRectangle(spriteBatch, tformedPullPos - pullJointWidgetSize / 2, pullJointWidgetSize, GUIStyle.Red,
true);
4455 DrawWidget(spriteBatch, tformedPullPos, WidgetType.Rectangle, 8, Color.Cyan, $
"IK ({limb.Name})", () =>
4457 if (!selectedLimbs.Contains(limb))
4459 selectedLimbs.Add(limb);
4460 ResetParamsEditor();
4462 limb.PullJointWorldAnchorA = ScreenToSim(PlayerInput.MousePosition);
4463 TryUpdateLimbParam(limb,
"pullpos", ConvertUnits.ToDisplayUnits(limb.PullJointLocalAnchorA / limb.Params.Scale / limb.Params.Ragdoll.LimbScale));
4464 GUI.DrawLine(spriteBatch, SimToScreen(limb.SimPosition), tformedPullPos, Color.MediumPurple);
4468 foreach (var joint
in character.AnimController.LimbJoints)
4470 Vector2 jointPos = Vector2.Zero;
4471 Vector2 otherPos = Vector2.Zero;
4472 Vector2 anchorPosA = ConvertUnits.ToDisplayUnits(joint.LocalAnchorA);
4473 Vector2 anchorPosB = ConvertUnits.ToDisplayUnits(joint.LocalAnchorB);
4474 if (joint.BodyA == limb.body.FarseerBody)
4476 jointPos = anchorPosA;
4477 otherPos = anchorPosB;
4479 else if (joint.BodyB == limb.body.FarseerBody)
4481 jointPos = anchorPosB;
4482 otherPos = anchorPosA;
4488 Vector2 limbScreenPos = cam.
WorldToScreen(limb.DrawPosition);
4489 var f = Vector2.Transform(jointPos, Matrix.CreateRotationZ(limb.Rotation));
4491 Vector2 tformedJointPos = limbScreenPos + f * Cam.Zoom;
4494 ShapeExtensions.DrawPoint(spriteBatch, limbScreenPos, Color.Black, size: 5);
4495 ShapeExtensions.DrawPoint(spriteBatch, limbScreenPos, Color.White, size: 1);
4496 GUI.DrawLine(spriteBatch, limbScreenPos, tformedJointPos, Color.Black, width: 3);
4497 GUI.DrawLine(spriteBatch, limbScreenPos, tformedJointPos, Color.White, width: 1);
4501 if (altDown && joint.BodyA == limb.body.FarseerBody)
4505 if (!altDown && joint.BodyB == limb.body.FarseerBody)
4509 var selectionWidget = GetJointSelectionWidget($
"{joint.Params.Name} selection widget ragdoll", joint);
4510 selectionWidget.DrawPos = tformedJointPos;
4511 selectionWidget.Draw(spriteBatch, deltaTime);
4512 if (selectedJoints.Contains(joint))
4514 if (joint.LimitEnabled && jointCreationMode == JointCreationMode.None)
4516 var otherBody = limb == joint.LimbA ? joint.LimbB : joint.LimbA;
4517 float rotation = -otherBody.Rotation + limb.Params.GetSpriteOrientation();
4518 if (character.AnimController.Dir < 0)
4520 rotation -= MathHelper.Pi;
4522 DrawJointLimitWidgets(spriteBatch, limb, joint, tformedJointPos, autoFreeze:
true, allowPairEditing:
true, rotationOffset: rotation, holdPosition:
true);
4524 Limb referenceLimb = altDown ? joint.LimbB : joint.LimbA;
4526 Vector2 to = tformedJointPos - VectorExtensions.ForwardFlipped(referenceLimb.Rotation - referenceLimb.Params.GetSpriteOrientation(), 150);
4527 GUI.DrawLine(spriteBatch, tformedJointPos, to, Color.LightGray * 0.7f, width: 2);
4528 var dotSize =
new Vector2(5, 5);
4529 var rect =
new Rectangle((tformedJointPos - dotSize / 2).ToPoint(), dotSize.ToPoint());
4535 string tooltip = $
"{joint.Params.Name} {jointPos.FormatZeroDecimal()}";
4536 GUI.DrawString(spriteBatch, tformedJointPos -
new Vector2(1.2f, 0.5f) * GUIStyle.Font.MeasureString(tooltip), tooltip, Color.White, Color.Black * 0.5f);
4537 if (PlayerInput.PrimaryMouseButtonHeld())
4539 if (!selectionWidget.IsControlled) {
continue; }
4540 if (jointCreationMode != JointCreationMode.None) {
continue; }
4547 character.AnimController.Collider.PhysEnabled =
false;
4549 Vector2 input = ConvertUnits.ToSimUnits(scaledMouseSpeed) / Cam.Zoom;
4551 input = input.TransformVector(VectorExtensions.ForwardFlipped(limb.Rotation));
4552 if (joint.BodyA == limb.body.FarseerBody)
4554 joint.LocalAnchorA += input;
4555 Vector2 transformedValue = ConvertUnits.ToDisplayUnits(joint.LocalAnchorA / joint.Scale);
4556 TryUpdateJointParam(joint,
"limb1anchor", transformedValue);
4558 if (copyJointSettings)
4560 foreach (var j
in selectedJoints)
4562 j.LocalAnchorA = joint.LocalAnchorA;
4563 TryUpdateJointParam(j,
"limb1anchor", transformedValue);
4567 else if (joint.BodyB == limb.body.FarseerBody)
4569 joint.LocalAnchorB += input;
4570 Vector2 transformedValue = ConvertUnits.ToDisplayUnits(joint.LocalAnchorB / joint.Scale);
4571 TryUpdateJointParam(joint,
"limb2anchor", transformedValue);
4573 if (copyJointSettings)
4575 foreach (var j
in selectedJoints)
4577 j.LocalAnchorB = joint.LocalAnchorB;
4578 TryUpdateJointParam(j,
"limb2anchor", transformedValue);
4583 if (limbPairEditing)
4585 UpdateOtherJoints(limb, (otherLimb, otherJoint) =>
4587 if (joint.BodyA == limb.body.FarseerBody && otherJoint.BodyA == otherLimb.body.FarseerBody)
4589 otherJoint.LocalAnchorA = joint.LocalAnchorA;
4590 TryUpdateJointParam(otherJoint,
"limb1anchor", ConvertUnits.ToDisplayUnits(joint.LocalAnchorA / joint.Scale));
4592 else if (joint.BodyB == limb.body.FarseerBody && otherJoint.BodyB == otherLimb.body.FarseerBody)
4594 otherJoint.LocalAnchorB = joint.LocalAnchorB;
4595 TryUpdateJointParam(otherJoint,
"limb2anchor", ConvertUnits.ToDisplayUnits(joint.LocalAnchorB / joint.Scale));
4602 isFrozen = freezeToggle.Selected;
4603 character.AnimController.Collider.PhysEnabled =
true;
4611 private void UpdateOtherLimbs(Limb limb, Action<Limb> updateAction)
4614 if (limbPairEditing)
4616 string limbType = limb.type.ToString();
4617 bool isLeft = limbType.Contains(
"Left");
4618 bool isRight = limbType.Contains(
"Right");
4619 if (isLeft || isRight)
4621 if (character.AnimController.HasMultipleLimbsOfSameType)
4623 GetOtherLimbs(limb)?.ForEach(l => UpdateOtherLimbs(l));
4627 Limb otherLimb = GetOtherLimb(limbType, isLeft);
4628 if (otherLimb !=
null)
4630 UpdateOtherLimbs(otherLimb);
4633 void UpdateOtherLimbs(Limb otherLimb)
4635 updateAction(otherLimb);
4641 private void UpdateOtherJoints(Limb limb, Action<Limb, LimbJoint> updateAction)
4644 if (limbPairEditing)
4646 string limbType = limb.type.ToString();
4647 bool isLeft = limbType.Contains(
"Left");
4648 bool isRight = limbType.Contains(
"Right");
4649 if (isLeft || isRight)
4651 if (character.AnimController.HasMultipleLimbsOfSameType)
4653 GetOtherLimbs(limb)?.ForEach(l => UpdateOtherJoints(l));
4657 Limb otherLimb = GetOtherLimb(limbType, isLeft);
4658 if (otherLimb !=
null)
4660 UpdateOtherJoints(otherLimb);
4663 void UpdateOtherJoints(Limb otherLimb)
4665 foreach (var otherJoint
in character.AnimController.LimbJoints)
4667 updateAction(otherLimb, otherJoint);
4674 private Limb GetOtherLimb(
string limbType,
bool isLeft)
4676 string otherLimbType = isLeft ? limbType.Replace(
"Left",
"Right") : limbType.Replace(
"Right",
"Left");
4677 if (Enum.TryParse(otherLimbType, out LimbType type))
4679 return character.AnimController.GetLimb(type);
4685 private IEnumerable<Limb> GetOtherLimbs(Limb limb)
4687 var otherLimbs = character.AnimController.Limbs.Where(l => l.type == limb.type && l != limb);
4688 string limbType = limb.type.ToString();
4689 string otherLimbType = limbType.Contains(
"Left") ? limbType.Replace(
"Left",
"Right") : limbType.Replace(
"Right",
"Left");
4690 if (Enum.TryParse(otherLimbType, out LimbType type))
4692 otherLimbs = otherLimbs.Union(character.AnimController.Limbs.Where(l => l.type == type));
4699 private List<Texture2D> textures;
4700 private List<Texture2D> Textures
4704 if (textures ==
null)
4711 private List<string> texturePaths;
4712 private void CreateTextures()
4714 textures =
new List<Texture2D>();
4715 texturePaths =
new List<string>();
4716 foreach (Limb limb
in character.AnimController.Limbs)
4718 if (limb.ActiveSprite ==
null || texturePaths.Contains(limb.ActiveSprite.FilePath.Value)) {
continue; }
4719 if (limb.ActiveSprite.Texture ==
null) {
continue; }
4720 textures.Add(limb.ActiveSprite.Texture);
4721 texturePaths.Add(limb.ActiveSprite.FilePath.Value);
4725 private void DrawSpritesheetEditor(SpriteBatch spriteBatch,
float deltaTime)
4727 int offsetX = spriteSheetOffsetX;
4728 int offsetY = spriteSheetOffsetY;
4729 for (
int i = 0; i < Textures.Count; i++)
4731 var texture = Textures[i];
4734 spriteBatch.Draw(texture,
4735 position:
new Vector2(offsetX, offsetY),
4737 origin: Vector2.Zero,
4738 sourceRectangle:
null,
4739 scale: spriteSheetZoom,
4740 effects: SpriteEffects.None,
4744 GUI.DrawRectangle(spriteBatch,
new Vector2(offsetX, offsetY), texture.Bounds.Size.ToVector2() * spriteSheetZoom, Color.White);
4745 foreach (Limb limb
in character.AnimController.Limbs)
4747 if (limb.ActiveSprite ==
null || limb.ActiveSprite.FilePath != texturePaths[i]) {
continue; }
4748 Rectangle rect = limb.ActiveSprite.SourceRect;
4749 rect.Size = rect.MultiplySize(spriteSheetZoom);
4750 rect.Location = rect.Location.Multiply(spriteSheetZoom);
4753 Vector2 origin = limb.ActiveSprite.Origin;
4754 Vector2 limbScreenPos =
new Vector2(rect.X + origin.X * spriteSheetZoom, rect.Y + origin.Y * spriteSheetZoom);
4756 foreach (var wearable
in limb.WearingItems)
4758 Vector2 orig = limb.ActiveSprite.Origin;
4759 if (!wearable.InheritOrigin)
4761 orig = wearable.Sprite.Origin;
4763 if (limb.body.Dir == -1.0f)
4765 orig.X = wearable.Sprite.SourceRect.Width - orig.X;
4768 spriteBatch.Draw(wearable.Sprite.Texture,
4769 position: limbScreenPos,
4772 sourceRectangle: wearable.InheritSourceRect ? limb.ActiveSprite.SourceRect : wearable.Sprite.SourceRect,
4773 scale: (wearable.InheritScale ? 1 : wearable.Scale / RagdollParams.TextureScale) * spriteSheetZoom,
4774 effects: SpriteEffects.None,
4779 if (character.AnimController.Dir < 0)
4781 limbScreenPos.X = rect.X + rect.Width - (float)Math.Round(origin.X * spriteSheetZoom);
4785 DrawSpritesheetJointEditor(spriteBatch, deltaTime, limb, limbScreenPos);
4787 bool isMouseOn = rect.Contains(PlayerInput.MousePosition);
4791 int halfSize = widgetSize / 2;
4792 Vector2 stringOffset =
new Vector2(5, 14);
4793 var topLeft = rect.Location.ToVector2();
4794 var topRight =
new Vector2(topLeft.X + rect.Width, topLeft.Y);
4795 var bottomRight =
new Vector2(topRight.X, topRight.Y + rect.Height);
4796 bool isSelected = selectedLimbs.Contains(limb);
4797 if (jointStartLimb != limb && jointEndLimb != limb)
4799 if (isSelected || !onlyShowSourceRectForSelectedLimbs)
4801 GUI.DrawRectangle(spriteBatch, rect, isSelected ? Color.Yellow : (isMouseOn ? Color.White : GUIStyle.Red));
4806 var sprite = limb.ActiveSprite;
4807 Vector2 GetTopLeft() => sprite.SourceRect.Location.ToVector2();
4808 Vector2 GetTopRight() =>
new Vector2(GetTopLeft().X + sprite.SourceRect.Width, GetTopLeft().Y);
4809 Vector2 GetBottomRight() =>
new Vector2(GetTopRight().X, GetTopRight().Y + sprite.SourceRect.Height);
4810 var originWidget = GetLimbEditWidget($
"{limb.Params.ID}_origin", limb, widgetSize,
WidgetShape.Cross, initMethod: w =>
4812 w.Refresh = () => w.Tooltip = $
"{GetCharacterEditorTranslation("Origin
")}: {sprite.RelativeOrigin.FormatDoubleDecimal()}";
4814 w.MouseHeld += dTime =>
4816 var spritePos = new Vector2(spriteSheetOffsetX, GetOffsetY(limb.ActiveSprite));
4817 w.DrawPos = PlayerInput.MousePosition.Clamp(spritePos + GetTopLeft() * spriteSheetZoom, spritePos + GetBottomRight() * spriteSheetZoom);
4818 sprite.Origin = (w.DrawPos - spritePos - sprite.SourceRect.Location.ToVector2() * spriteSheetZoom) / spriteSheetZoom;
4819 if (limb.DamagedSprite != null)
4821 limb.DamagedSprite.RelativeOrigin = sprite.RelativeOrigin;
4823 TryUpdateLimbParam(limb,
"origin", sprite.RelativeOrigin);
4824 if (limbPairEditing)
4826 UpdateOtherLimbs(limb, otherLimb =>
4828 otherLimb.ActiveSprite.RelativeOrigin = sprite.RelativeOrigin;
4829 if (otherLimb.DamagedSprite != null)
4831 otherLimb.DamagedSprite.RelativeOrigin = sprite.RelativeOrigin;
4833 TryUpdateLimbParam(otherLimb,
"origin", sprite.RelativeOrigin);
4837 w.PreUpdate += dTime =>
4842 w.Enabled = !lockSpriteOrigin;
4845 w.PreDraw += (sb, dTime) =>
4847 var spritePos =
new Vector2(spriteSheetOffsetX, GetOffsetY(limb.ActiveSprite));
4848 w.DrawPos = (spritePos + (sprite.Origin + sprite.SourceRect.Location.ToVector2()) * spriteSheetZoom)
4849 .Clamp(spritePos + GetTopLeft() * spriteSheetZoom, spritePos + GetBottomRight() * spriteSheetZoom);
4853 originWidget.Draw(spriteBatch, deltaTime);
4854 if (!lockSpritePosition && (limb.type !=
LimbType.Head || !character.IsHuman))
4856 var positionWidget = GetLimbEditWidget($
"{limb.Params.ID}_position", limb, widgetSize,
WidgetShape.Rectangle, initMethod: w =>
4858 w.Refresh = () => w.Tooltip = $
"{GetCharacterEditorTranslation("Position
")}: {limb.ActiveSprite.SourceRect.Location}";
4860 w.MouseHeld += dTime =>
4862 w.DrawPos = PlayerInput.MousePosition;
4863 Sprite activeSprite = limb.ActiveSprite;
4864 var newRect = activeSprite.SourceRect;
4865 newRect.Location = new Point(
4866 (int)((PlayerInput.MousePosition.X + halfSize - spriteSheetOffsetX) / spriteSheetZoom),
4867 (int)((PlayerInput.MousePosition.Y + halfSize - GetOffsetY(activeSprite)) / spriteSheetZoom));
4868 activeSprite.SourceRect = newRect;
4869 if (limb.DamagedSprite != null)
4871 limb.DamagedSprite.SourceRect = activeSprite.SourceRect;
4873 TryUpdateLimbParam(limb,
"sourcerect", newRect);
4874 var spritePos = new Vector2(spriteSheetOffsetX, GetOffsetY(activeSprite));
4875 Vector2 newOrigin = (originWidget.DrawPos - spritePos - activeSprite.SourceRect.Location.ToVector2() * spriteSheetZoom) / spriteSheetZoom;
4876 RecalculateOrigin(limb, newOrigin);
4877 if (limbPairEditing)
4879 UpdateOtherLimbs(limb, otherLimb =>
4881 otherLimb.ActiveSprite.SourceRect = newRect;
4882 if (otherLimb.DamagedSprite != null)
4884 otherLimb.DamagedSprite.SourceRect = newRect;
4886 TryUpdateLimbParam(otherLimb,
"sourcerect", newRect);
4887 RecalculateOrigin(otherLimb, newOrigin);
4891 w.PreDraw += (sb, dTime) => w.Refresh();
4893 if (!positionWidget.IsControlled)
4895 positionWidget.DrawPos = topLeft -
new Vector2(halfSize);
4897 positionWidget.Draw(spriteBatch, deltaTime);
4899 if (!lockSpriteSize && (limb.type !=
LimbType.Head || !character.IsHuman))
4901 var sizeWidget = GetLimbEditWidget($
"{limb.Params.ID}_size", limb, widgetSize,
WidgetShape.Rectangle, initMethod: w =>
4903 w.Refresh = () => w.Tooltip = $
"{GetCharacterEditorTranslation("Size
")}: {limb.ActiveSprite.SourceRect.Size}";
4905 w.MouseHeld += dTime =>
4907 w.DrawPos = PlayerInput.MousePosition;
4908 Sprite activeSprite = limb.ActiveSprite;
4909 Rectangle newRect = activeSprite.SourceRect;
4910 float offset_y = activeSprite.SourceRect.Y * spriteSheetZoom + GetOffsetY(activeSprite);
4911 float offset_x = activeSprite.SourceRect.X * spriteSheetZoom + spriteSheetOffsetX;
4912 int width = (int)((PlayerInput.MousePosition.X - halfSize - offset_x) / spriteSheetZoom);
4913 int height = (int)((PlayerInput.MousePosition.Y - halfSize - offset_y) / spriteSheetZoom);
4914 newRect.Size = new Point(width, height);
4915 activeSprite.SourceRect = newRect;
4916 activeSprite.size = new Vector2(width, height);
4917 Vector2 colliderSize = new Vector2(ConvertUnits.ToSimUnits(width), ConvertUnits.ToSimUnits(height));
4918 if (recalculateCollider)
4920 RecalculateCollider(limb, colliderSize);
4922 RecalculateOrigin(limb);
4923 if (limb.DamagedSprite != null)
4925 limb.DamagedSprite.SourceRect = activeSprite.SourceRect;
4927 TryUpdateLimbParam(limb,
"sourcerect", newRect);
4928 if (limbPairEditing)
4930 UpdateOtherLimbs(limb, otherLimb =>
4932 otherLimb.ActiveSprite.SourceRect = newRect;
4933 RecalculateOrigin(otherLimb);
4934 if (recalculateCollider)
4936 RecalculateCollider(otherLimb, colliderSize);
4938 if (otherLimb.DamagedSprite != null)
4940 otherLimb.DamagedSprite.SourceRect = newRect;
4942 TryUpdateLimbParam(otherLimb,
"sourcerect", newRect);
4946 w.PreDraw += (sb, dTime) => w.Refresh();
4948 if (!sizeWidget.IsControlled)
4950 sizeWidget.DrawPos = bottomRight +
new Vector2(halfSize);
4952 sizeWidget.Draw(spriteBatch, deltaTime);
4955 else if (isMouseOn && GUI.MouseOn ==
null && Widget.SelectedWidgets.None())
4958 GUI.DrawString(spriteBatch, limbScreenPos +
new Vector2(10, -10), limb.Name, Color.White, Color.Black * 0.5f);
4963 GUI.DrawRectangle(spriteBatch, rect, isMouseOn ? Color.White : Color.Gray);
4964 if (isMouseOn && GUI.MouseOn ==
null && Widget.SelectedWidgets.None())
4967 GUI.DrawString(spriteBatch, limbScreenPos +
new Vector2(10, -10), limb.Name, Color.White, Color.Black * 0.5f);
4971 offsetY += (int)(texture.Height * spriteSheetZoom);
4975 private int GetTextureHeight(Sprite sprite)
4977 int textureIndex = Textures.IndexOf(sprite.Texture);
4979 foreach (var t
in Textures)
4981 if (Textures.IndexOf(t) < textureIndex)
4986 return (
int)(height * spriteSheetZoom);
4989 private int GetOffsetY(Sprite sprite) => spriteSheetOffsetY + GetTextureHeight(sprite);
4991 private void RecalculateCollider(Limb l, Vector2 size)
4994 float multiplier = 0.9f;
4995 l.body.SetSize(
new Vector2(size.X, size.Y) * l.Scale * RagdollParams.TextureScale * multiplier);
4996 TryUpdateLimbParam(l,
"radius", ConvertUnits.ToDisplayUnits(l.body.Radius / l.Params.Scale / RagdollParams.LimbScale / RagdollParams.TextureScale));
4997 TryUpdateLimbParam(l,
"width", ConvertUnits.ToDisplayUnits(l.body.Width / l.Params.Scale / RagdollParams.LimbScale / RagdollParams.TextureScale));
4998 TryUpdateLimbParam(l,
"height", ConvertUnits.ToDisplayUnits(l.body.Height / l.Params.Scale / RagdollParams.LimbScale / RagdollParams.TextureScale));
5001 private void RecalculateOrigin(Limb l, Vector2? newOrigin =
null)
5003 Sprite activeSprite = l.ActiveSprite;
5004 if (lockSpriteOrigin)
5007 activeSprite.Origin = newOrigin ?? activeSprite.Origin;
5008 TryUpdateLimbParam(l,
"origin", activeSprite.RelativeOrigin);
5013 activeSprite.RelativeOrigin = activeSprite.RelativeOrigin;
5017 private void DrawSpritesheetJointEditor(SpriteBatch spriteBatch,
float deltaTime, Limb limb, Vector2 limbScreenPos,
float spriteRotation = 0)
5019 foreach (var joint
in character.AnimController.LimbJoints)
5021 Vector2 jointPos = Vector2.Zero;
5022 Vector2 anchorPosA = ConvertUnits.ToDisplayUnits(joint.LocalAnchorA);
5023 Vector2 anchorPosB = ConvertUnits.ToDisplayUnits(joint.LocalAnchorB);
5026 if (joint.BodyA == limb.body.FarseerBody)
5028 jointPos = anchorPosA;
5032 else if (joint.BodyB == limb.body.FarseerBody)
5034 jointPos = anchorPosB;
5042 Vector2 tformedJointPos = jointPos = jointPos / joint.Scale / limb.TextureScale * spriteSheetZoom;
5043 tformedJointPos.Y = -tformedJointPos.Y;
5044 tformedJointPos.X *= character.AnimController.Dir;
5045 tformedJointPos += limbScreenPos;
5046 var jointSelectionWidget = GetJointSelectionWidget($
"{joint.Params.Name} selection widget {anchorID}", joint, $
"{joint.Params.Name} selection widget {otherID}");
5047 jointSelectionWidget.DrawPos = tformedJointPos;
5048 jointSelectionWidget.Draw(spriteBatch, deltaTime);
5049 var otherWidget = GetJointSelectionWidget($
"{joint.Params.Name} selection widget {otherID}", joint, $
"{joint.Params.Name} selection widget {anchorID}");
5050 if (anchorID ==
"2")
5052 bool isSelected = selectedJoints.Contains(joint);
5053 bool isHovered = jointSelectionWidget.IsSelected || otherWidget.IsSelected;
5054 if (isSelected || isHovered)
5056 GUI.DrawLine(spriteBatch, jointSelectionWidget.DrawPos, otherWidget.DrawPos, jointSelectionWidget.Color, width: 2);
5059 if (selectedJoints.Contains(joint))
5061 if (joint.LimitEnabled && jointCreationMode == JointCreationMode.None)
5063 DrawJointLimitWidgets(spriteBatch, limb, joint, tformedJointPos, autoFreeze:
false, allowPairEditing:
true, holdPosition:
false, rotationOffset: joint.LimbB.Params.GetSpriteOrientation());
5065 if (jointSelectionWidget.IsControlled)
5067 Vector2 input = ConvertUnits.ToSimUnits(scaledMouseSpeed);
5069 input.X *= character.AnimController.Dir;
5070 input *= joint.Scale * limb.TextureScale / spriteSheetZoom;
5071 if (joint.BodyA == limb.body.FarseerBody)
5073 joint.LocalAnchorA += input;
5074 Vector2 transformedValue = ConvertUnits.ToDisplayUnits(joint.LocalAnchorA / joint.Scale);
5075 TryUpdateJointParam(joint,
"limb1anchor", transformedValue);
5077 if (copyJointSettings)
5079 foreach (var j
in selectedJoints)
5081 j.LocalAnchorA = joint.LocalAnchorA;
5082 TryUpdateJointParam(j,
"limb1anchor", transformedValue);
5086 else if (joint.BodyB == limb.body.FarseerBody)
5088 joint.LocalAnchorB += input;
5089 Vector2 transformedValue = ConvertUnits.ToDisplayUnits(joint.LocalAnchorB / joint.Scale);
5090 TryUpdateJointParam(joint,
"limb2anchor", transformedValue);
5092 if (copyJointSettings)
5094 foreach (var j
in selectedJoints)
5096 j.LocalAnchorB = joint.LocalAnchorB;
5097 TryUpdateJointParam(j,
"limb2anchor", transformedValue);
5101 if (limbPairEditing)
5103 UpdateOtherJoints(limb, (otherLimb, otherJoint) =>
5105 if (joint.BodyA == limb.body.FarseerBody && otherJoint.BodyA == otherLimb.body.FarseerBody)
5107 otherJoint.LocalAnchorA = joint.LocalAnchorA;
5108 TryUpdateJointParam(otherJoint,
"limb1anchor", ConvertUnits.ToDisplayUnits(joint.LocalAnchorA / joint.Scale));
5110 else if (joint.BodyB == limb.body.FarseerBody && otherJoint.BodyB == otherLimb.body.FarseerBody)
5112 otherJoint.LocalAnchorB = joint.LocalAnchorB;
5113 TryUpdateJointParam(otherJoint,
"limb2anchor", ConvertUnits.ToDisplayUnits(joint.LocalAnchorB / joint.Scale));
5122 private void DrawJointLimitWidgets(SpriteBatch spriteBatch, Limb limb, LimbJoint joint, Vector2 drawPos,
bool autoFreeze,
bool allowPairEditing,
bool holdPosition,
float rotationOffset = 0)
5124 bool clockWise = joint.Params.ClockWiseRotation;
5125 Color angleColor = joint.UpperLimit - joint.LowerLimit > 0 ? GUIStyle.Green * 0.5f : GUIStyle.Red;
5126 DrawRadialWidget(spriteBatch, drawPos, MathHelper.ToDegrees(joint.UpperLimit), $
"{joint.Params.Name}: {GetCharacterEditorTranslation("UpperLimit
")}", Color.Cyan, angle =>
5128 joint.UpperLimit = MathHelper.ToRadians(angle);
5129 ValidateJoint(joint);
5130 angle = MathHelper.ToDegrees(joint.UpperLimit);
5131 TryUpdateJointParam(joint,
"upperlimit", angle);
5132 if (copyJointSettings)
5134 foreach (var j in selectedJoints)
5136 if (j.LimitEnabled != joint.LimitEnabled)
5138 j.LimitEnabled = joint.LimitEnabled;
5139 TryUpdateJointParam(j,
"limitenabled", j.LimitEnabled);
5141 j.UpperLimit = joint.UpperLimit;
5142 TryUpdateJointParam(j,
"upperlimit", angle);
5145 if (allowPairEditing && limbPairEditing)
5147 UpdateOtherJoints(limb, (otherLimb, otherJoint) =>
5149 if (IsMatchingLimb(limb, otherLimb, joint, otherJoint))
5151 if (otherJoint.LimitEnabled != joint.LimitEnabled)
5153 otherJoint.LimitEnabled = otherJoint.LimitEnabled;
5154 TryUpdateJointParam(otherJoint,
"limitenabled", otherJoint.LimitEnabled);
5156 otherJoint.UpperLimit = joint.UpperLimit;
5157 TryUpdateJointParam(otherJoint,
"upperlimit", angle);
5161 DrawAngle(20, angleColor, 4);
5162 DrawAngle(40, Color.Cyan);
5163 GUI.DrawString(spriteBatch, drawPos, angle.FormatZeroDecimal(), Color.Black, backgroundColor: Color.Cyan, font: GUIStyle.SmallFont);
5164 }, circleRadius: 40, rotationOffset: rotationOffset, displayAngle:
false, clockWise: clockWise, holdPosition: holdPosition);
5165 DrawRadialWidget(spriteBatch, drawPos, MathHelper.ToDegrees(joint.LowerLimit), $
"{joint.Params.Name}: {GetCharacterEditorTranslation("LowerLimit
")}", Color.Yellow, angle =>
5167 joint.LowerLimit = MathHelper.ToRadians(angle);
5168 ValidateJoint(joint);
5169 angle = MathHelper.ToDegrees(joint.LowerLimit);
5170 TryUpdateJointParam(joint,
"lowerlimit", angle);
5171 if (copyJointSettings)
5173 foreach (var j in selectedJoints)
5175 if (j.LimitEnabled != joint.LimitEnabled)
5177 j.LimitEnabled = joint.LimitEnabled;
5178 TryUpdateJointParam(j,
"limitenabled", j.LimitEnabled);
5180 j.LowerLimit = joint.LowerLimit;
5181 TryUpdateJointParam(j,
"lowerlimit", angle);
5184 if (allowPairEditing && limbPairEditing)
5186 UpdateOtherJoints(limb, (otherLimb, otherJoint) =>
5188 if (IsMatchingLimb(limb, otherLimb, joint, otherJoint))
5190 if (otherJoint.LimitEnabled != joint.LimitEnabled)
5192 otherJoint.LimitEnabled = otherJoint.LimitEnabled;
5193 TryUpdateJointParam(otherJoint,
"limitenabled", otherJoint.LimitEnabled);
5195 otherJoint.LowerLimit = joint.LowerLimit;
5196 TryUpdateJointParam(otherJoint,
"lowerlimit", angle);
5200 DrawAngle(20, angleColor, 4);
5201 DrawAngle(25, Color.Yellow);
5202 GUI.DrawString(spriteBatch, drawPos, angle.FormatZeroDecimal(), Color.Black, backgroundColor: Color.Yellow, font: GUIStyle.SmallFont);
5203 }, circleRadius: 25, rotationOffset: rotationOffset, displayAngle:
false, clockWise: clockWise, holdPosition: holdPosition);
5204 void DrawAngle(
float radius, Color color,
float thickness = 5)
5206 float angle = joint.UpperLimit - joint.LowerLimit;
5207 float offset = clockWise ? rotationOffset + joint.LowerLimit - MathHelper.PiOver2 : rotationOffset - joint.UpperLimit - MathHelper.PiOver2;
5208 ShapeExtensions.DrawSector(spriteBatch, drawPos, radius, angle, 40, color, offset: offset, thickness: thickness);
5212 private void Nudge(Keys key)
5217 foreach (var limb
in selectedLimbs)
5220 if (limb.type ==
LimbType.Head && character.IsHuman) {
continue; }
5221 var newRect = limb.ActiveSprite.SourceRect;
5222 bool resize = PlayerInput.KeyDown(Keys.LeftControl);
5225 if (lockSpriteSize) {
return; }
5230 if (lockSpritePosition) {
return; }
5233 UpdateSourceRect(limb, newRect, resize);
5237 foreach (var limb
in selectedLimbs)
5240 if (limb.type ==
LimbType.Head && character.IsHuman) {
continue; }
5241 var newRect = limb.ActiveSprite.SourceRect;
5242 bool resize = PlayerInput.KeyDown(Keys.LeftControl);
5245 if (lockSpriteSize) {
return; }
5250 if (lockSpritePosition) {
return; }
5253 UpdateSourceRect(limb, newRect, resize);
5257 foreach (var limb
in selectedLimbs)
5260 if (limb.type ==
LimbType.Head && character.IsHuman) {
continue; }
5261 var newRect = limb.ActiveSprite.SourceRect;
5262 bool resize = PlayerInput.KeyDown(Keys.LeftControl);
5265 if (lockSpriteSize) {
return; }
5270 if (lockSpritePosition) {
return; }
5273 UpdateSourceRect(limb, newRect, resize);
5277 foreach (var limb
in selectedLimbs)
5280 if (limb.type ==
LimbType.Head && character.IsHuman) {
continue; }
5281 var newRect = limb.ActiveSprite.SourceRect;
5282 bool resize = PlayerInput.KeyDown(Keys.LeftControl);
5285 if (lockSpriteSize) {
return; }
5290 if (lockSpritePosition) {
return; }
5293 UpdateSourceRect(limb, newRect, resize);
5297 RagdollParams.StoreSnapshot();
5300 private void SetSpritesheetRestriction(
bool value)
5302 unrestrictSpritesheet = value;
5303 CalculateSpritesheetZoom();
5304 spriteSheetZoomBar.BarScroll = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(spriteSheetMinZoom, spriteSheetMaxZoom, spriteSheetZoom));
5308 #region Widgets as methods
5309 private void DrawRadialWidget(SpriteBatch spriteBatch, Vector2 drawPos,
float value, LocalizedString toolTip, Color color, Action<float> onClick,
5310 float circleRadius = 30,
int widgetSize = 10,
float rotationOffset = 0,
bool clockWise =
true,
bool displayAngle =
true,
bool? autoFreeze =
null,
bool wrapAnglePi =
false,
bool holdPosition =
false,
int rounding = 1)
5313 if (!MathUtils.IsValid(angle))
5317 float drawAngle = clockWise ? angle : -angle;
5318 var widgetDrawPos = drawPos + VectorExtensions.Forward(MathHelper.ToRadians(drawAngle) + rotationOffset - MathHelper.PiOver2, circleRadius);
5319 GUI.DrawLine(spriteBatch, drawPos, widgetDrawPos, color);
5320 DrawWidget(spriteBatch, widgetDrawPos, WidgetType.Rectangle, widgetSize, color, toolTip, () =>
5322 GUI.DrawLine(spriteBatch, drawPos, widgetDrawPos, color, width: 3);
5323 ShapeExtensions.DrawCircle(spriteBatch, drawPos, circleRadius, 40, color, thickness: 1);
5324 Vector2 d = PlayerInput.MousePosition - drawPos;
5325 float newAngle = clockWise
5326 ? MathUtils.VectorToAngle(d) + MathHelper.PiOver2 - rotationOffset
5327 : -MathUtils.VectorToAngle(d) - MathHelper.PiOver2 + rotationOffset;
5328 angle = MathHelper.ToDegrees(wrapAnglePi ? MathUtils.WrapAnglePi(newAngle) : MathUtils.WrapAngleTwoPi(newAngle));
5329 angle = (float)Math.Round(angle / rounding) * rounding;
5330 if (angle >= 360 || angle <= -360) { angle = 0; }
5333 GUI.DrawString(spriteBatch, drawPos, angle.FormatZeroDecimal(), Color.Black, backgroundColor: color, font: GUIStyle.SmallFont);
5336 }, autoFreeze, holdPosition, onHovered: () =>
5338 if (!PlayerInput.PrimaryMouseButtonHeld())
5340 GUIComponent.DrawToolTip(
5342 $
"{toolTip} ({angle.FormatZeroDecimal()})",
5343 new Vector2(drawPos.X + 50, drawPos.Y - widgetSize / 2 - 50));
5349 private void DrawWidget(SpriteBatch spriteBatch, Vector2 drawPos, WidgetType widgetType,
int size, Color color, LocalizedString toolTip, Action onPressed,
bool? autoFreeze =
null,
bool holdPosition =
false, Action onHovered =
null)
5351 var drawRect =
new Rectangle((
int)drawPos.X - size / 2, (
int)drawPos.Y - size / 2, size, size);
5352 var inputRect = drawRect;
5353 inputRect.Inflate(size * 0.75f, size * 0.75f);
5354 bool isMouseOn = inputRect.Contains(PlayerInput.MousePosition);
5355 bool isSelected = isMouseOn && GUI.MouseOn ==
null && Widget.SelectedWidgets.None();
5358 case WidgetType.Rectangle:
5361 var rect = drawRect;
5362 rect.Inflate(size * 0.3f, size * 0.3f);
5363 GUI.DrawRectangle(spriteBatch, rect, color, thickness: 3, isFilled: PlayerInput.PrimaryMouseButtonHeld());
5367 GUI.DrawRectangle(spriteBatch, drawRect, color, thickness: 1, isFilled:
false);
5370 case WidgetType.Circle:
5373 ShapeExtensions.DrawCircle(spriteBatch, drawPos, size * 0.7f, 40, color, thickness: 3);
5377 ShapeExtensions.DrawCircle(spriteBatch, drawPos, size * 0.5f, 40, color, thickness: 1);
5380 default:
throw new NotImplementedException(widgetType.ToString());
5385 if (onHovered ==
null)
5387 GUIComponent.DrawToolTip(spriteBatch, toolTip,
new Vector2(drawRect.Right + 5, drawRect.Y - drawRect.Height / 2));
5393 if (PlayerInput.PrimaryMouseButtonHeld())
5395 if (autoFreeze ?? this.autoFreeze)
5399 if (holdPosition ==
true)
5401 character.AnimController.Collider.PhysEnabled =
false;
5407 isFrozen = freezeToggle.Selected;
5408 character.AnimController.Collider.PhysEnabled =
true;
5411 if (PlayerInput.PrimaryMouseButtonClicked())
5419 #region Widgets as classes
5420 private Dictionary<string, Widget> animationWidgets =
new Dictionary<string, Widget>();
5421 private Dictionary<string, Widget> jointSelectionWidgets =
new Dictionary<string, Widget>();
5422 private Dictionary<string, Widget> limbEditWidgets =
new Dictionary<string, Widget>();
5424 private Widget GetAnimationWidget(
string name, Color innerColor, Color? outerColor =
null,
int size = 10,
float sizeMultiplier = 2, WidgetShape shape =
WidgetShape.Rectangle, Action<Widget> initMethod =
null)
5426 string id = $
"{character.SpeciesName}_{character.AnimController.CurrentAnimationParams.AnimationType.ToString()}_{name}";
5427 if (!animationWidgets.TryGetValue(
id, out Widget widget))
5429 int selectedSize = (int)Math.Round(size * sizeMultiplier);
5430 widget =
new Widget(
id, size, shape)
5432 TooltipOffset =
new Vector2(selectedSize / 2 + 5, -10),
5433 Data = character.AnimController.CurrentAnimationParams
5435 widget.MouseUp += () => CurrentAnimation.StoreSnapshot();
5436 widget.Color = innerColor;
5437 widget.SecondaryColor = outerColor;
5438 widget.PreUpdate += dTime =>
5440 widget.Enabled = editAnimations;
5443 AnimationParams data = widget.Data as AnimationParams;
5444 widget.Enabled = data.AnimationType == character.AnimController.CurrentAnimationParams.AnimationType;
5447 widget.PostUpdate += dTime =>
5449 widget.InputAreaMargin = widget.IsControlled ? 1000 : 0;
5450 widget.Size = widget.IsSelected ? selectedSize : size;
5451 widget.IsFilled = widget.IsControlled;
5453 widget.PreDraw += (sp, dTime) =>
5455 if (!widget.IsControlled)
5460 animationWidgets.Add(
id, widget);
5461 initMethod?.Invoke(widget);
5466 private Widget GetJointSelectionWidget(
string id, LimbJoint joint,
string linkedId =
null)
5469 if (!jointSelectionWidgets.TryGetValue(
id, out Widget jointWidget))
5471 jointWidget = CreateJointSelectionWidget(
id, joint);
5472 if (linkedId !=
null)
5474 if (!jointSelectionWidgets.TryGetValue(linkedId, out Widget linkedWidget))
5476 linkedWidget = CreateJointSelectionWidget(linkedId, joint);
5478 jointWidget.LinkedWidget = linkedWidget;
5479 linkedWidget.LinkedWidget = jointWidget;
5485 Widget CreateJointSelectionWidget(
string ID, LimbJoint j)
5487 int normalSize = 10;
5488 int selectedSize = 20;
5489 var widget =
new Widget(ID, normalSize,
WidgetShape.Circle);
5490 widget.Refresh = () =>
5492 widget.ShowTooltip = !selectedJoints.Contains(joint);
5493 widget.Color = selectedJoints.Contains(joint) ? Color.Yellow : GUIStyle.Red;
5496 widget.PreUpdate += dTime => widget.Enabled = editJoints;
5497 widget.PostUpdate += dTime =>
5499 widget.InputAreaMargin = widget.IsControlled ? 1000 : 0;
5500 widget.Size = widget.IsSelected ? selectedSize : normalSize;
5502 widget.MouseDown += () =>
5504 if (jointCreationMode != JointCreationMode.None) {
return; }
5505 if (!selectedJoints.Contains(joint))
5507 if (!Widget.EnableMultiSelect)
5509 selectedJoints.Clear();
5511 selectedJoints.Add(joint);
5513 else if (Widget.EnableMultiSelect)
5515 selectedJoints.Remove(joint);
5517 foreach (var w
in jointSelectionWidgets.Values)
5520 w.LinkedWidget?.Refresh();
5522 ResetParamsEditor();
5524 widget.MouseUp += () =>
5526 if (jointCreationMode == JointCreationMode.None)
5528 RagdollParams.StoreSnapshot();
5531 widget.Tooltip = joint.Params.Name;
5532 widget.TooltipOffset =
new Vector2(-GUIStyle.Font.MeasureString(widget.Tooltip).X - 30, -10);
5533 jointSelectionWidgets.Add(ID, widget);
5538 private Widget GetLimbEditWidget(
string ID, Limb limb,
int size = 5, WidgetShape shape =
WidgetShape.Rectangle, Action < Widget> initMethod =
null)
5540 if (!limbEditWidgets.TryGetValue(ID, out Widget widget))
5542 widget = CreateLimbEditWidget();
5543 limbEditWidgets.Add(ID, widget);
5547 Widget CreateLimbEditWidget()
5549 int normalSize = size;
5550 int selectedSize = (int)Math.Round(size * 1.5f);
5551 var w =
new Widget(ID, size, shape)
5553 TooltipOffset =
new Vector2(selectedSize / 2 + 5, -10),
5555 Color = Color.Yellow,
5556 SecondaryColor = Color.Gray,
5557 TextColor = Color.Yellow
5559 w.PreUpdate += dTime => w.Enabled = editLimbs && selectedLimbs.Contains(limb);
5560 w.PostUpdate += dTime =>
5562 w.InputAreaMargin = w.IsControlled ? 1000 : 0;
5563 w.Size = w.IsSelected ? selectedSize : normalSize;
5564 w.IsFilled = w.IsControlled;
5566 w.MouseUp += () => RagdollParams.StoreSnapshot();
5567 initMethod?.Invoke(w);
static Type GetParamTypeFromAnimType(AnimationType type, bool isHumanoid)
static AnimationParams Create(string fullPath, Identifier speciesName, AnimationType animationType, Type animationParamsType)
static string GetDefaultFile(Identifier speciesName, AnimationType animType)
abstract void StoreSnapshot()
static string GetDefaultFileName(Identifier speciesName, AnimationType animType)
static string GetFolder(Identifier speciesName)
Vector2 WorldToScreen(Vector2 coords)
void MoveCamera(float deltaTime, bool allowMove=true, bool allowZoom=true, bool allowInput=true, bool? followSub=null)
void UpdateTransform(bool interpolate=true, bool updateListener=true)
void PrepareCharacterCopy()
bool CreateCharacter(Identifier name, string mainFolder, bool isHumanoid, ContentPackage contentPackage, XElement ragdoll, XElement config=null, IEnumerable< AnimationParams > animations=null)
override void DeselectEditorSpecific()
override void Update(double deltaTime)
static LocalizedString GetCharacterEditorTranslation(string tag)
Character SpawnCharacter(Identifier speciesName, RagdollParams ragdoll=null)
override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
Character SpawnedCharacter
CursorState GetMouseCursorState()
override void AddToGUIUpdateList()
By default, submits the screen's main GUIFrame and, if requested upon construction,...
static CharacterEditorScreen Instance
void CopyExisting(CharacterParams character, RagdollParams ragdoll, IEnumerable< AnimationParams > animations)
void AddToGUIUpdateList()
static Character Create(CharacterInfo characterInfo, Vector2 position, string seed, ushort id=Entity.NullEntityID, bool isRemotePlayer=false, bool hasAi=true, RagdollParams ragdoll=null, bool spawnInitialItems=true)
Create a new character
Vector2? CursorWorldPosition
static readonly List< Character > CharacterList
static bool DisableControls
float ObstructVisionAmount
static Character? Controlled
readonly AnimController AnimController
void GiveJobItems(bool isPvPMode, WayPoint spawnPoint=null)
Stores information about the Character that is needed between rounds in the menu etc....
Contains character data that should be editable in the character editor.
bool Serialize(XElement element=null, bool alsoChildren=true, bool recursive=true)
static IEnumerable< ContentXElement > ConfigElements
static readonly Identifier HumanSpeciesName
static ContentPath FromRaw(string? rawValue)
virtual Vector2 WorldPosition
virtual void AddToGUIUpdateList(bool ignoreChildren=false, int order=0)
RectTransform RectTransform
static ContentPackage VanillaContent
static RasterizerState ScissorTestEnable
static int GraphicsHeight
static Lights.LightManager LightManager
Action ResolutionChanged
NOTE: Use very carefully. You need to ensure that you ALWAYS unsubscribe from this when you no longer...
static bool DevMode
Doesn't automatically enable los or bot AI or do anything like that. Probably not fully implemented.
static Sounds.SoundManager SoundManager
static readonly PrefabCollection< JobPrefab > Prefabs
readonly LimbParams Params
void DrawDamageModifiers(SpriteBatch spriteBatch, Camera cam, Vector2 startPos, bool isScreenSpace)
static readonly List< MapEntity > MapEntityList
static void ClearHighlightedEntities()
static File FromPath(string path, Type type)
Prefer FromPath<T> when possible, this just exists for cases where the type can only be decided at ru...
static ParamsEditor Instance
void SetPrevTransform(Vector2 simPosition, float rotation)
static List< PhysicsBody > List
float SpritesheetOrientation
bool Serialize(XElement element=null, bool alsoChildren=true, bool recursive=true)
static string GetDefaultFile(Identifier speciesName, ContentPackage contentPackage=null)
const string SoundCategoryWaterAmbience
void SetCategoryGainMultiplier(string category, float gain, int index=0)
static void DrawFront(SpriteBatch spriteBatch, bool editing=false, Predicate< MapEntity > predicate=null)
static Submarine MainSub
Note that this can be null in some situations, e.g. editors and missions that don't load a submarine.
static void CullEntities(Camera cam)
static void DrawBack(SpriteBatch spriteBatch, bool editing=false, Predicate< MapEntity > predicate=null)
void UpdateTransform(bool interpolate=true)
static WayPoint GetRandom(SpawnType spawnType=SpawnType.Human, JobPrefab assignedJob=null, Submarine sub=null, bool useSyncedRand=false, string spawnPointTag=null, bool ignoreSubmarine=false)