1 using Microsoft.Xna.Framework;
2 using Microsoft.Xna.Framework.Input;
3 using Microsoft.Xna.Framework.Graphics;
5 using System.Collections.Generic;
10 using FarseerPhysics.Dynamics;
37 private bool ShowExtraRagdollControls => editLimbs || editJoints;
41 private Vector2 spawnPosition;
43 private bool editCharacterInfo;
44 private bool editRagdoll;
45 private bool editAnimations;
46 private bool editLimbs;
47 private bool editJoints;
50 private bool drawSkeleton;
51 private bool drawDamageModifiers;
52 private bool showParamsEditor;
53 private bool showSpritesheet;
54 private bool isFrozen;
55 private bool autoFreeze;
56 private bool limbPairEditing;
57 private bool uniformScaling;
58 private bool lockSpriteOrigin;
59 private bool lockSpritePosition;
60 private bool lockSpriteSize;
61 private bool recalculateCollider;
62 private bool copyJointSettings;
63 private bool showColliders;
64 private bool displayWearables;
65 private bool displayBackgroundColor;
66 private bool onlyShowSourceRectForSelectedLimbs;
67 private bool unrestrictSpritesheet;
69 private enum JointCreationMode
76 private JointCreationMode jointCreationMode;
77 private bool isDrawingLimb;
80 private Limb jointStartLimb;
81 private Limb jointEndLimb;
82 private Vector2? anchor1Pos;
84 private const float holdTime = 0.2f;
85 private double holdTimer;
87 private float spriteSheetZoom = 1;
88 private float spriteSheetMinZoom = 0.25f;
89 private float spriteSheetMaxZoom = 1;
90 private const int spriteSheetOffsetY = 20;
91 private const int spriteSheetOffsetX = 30;
92 private bool hideBodySheet;
93 private Color backgroundColor =
new Color(0.2f, 0.2f, 0.2f, 1.0f);
94 private Vector2 cameraOffset;
96 private readonly List<LimbJoint> selectedJoints =
new List<LimbJoint>();
97 private readonly List<Limb> selectedLimbs =
new List<Limb>();
98 private readonly HashSet<Character> editedCharacters =
new HashSet<Character>();
100 private bool isEndlessRunner;
104 private Rectangle CalculateSpritesheetRectangle() =>
105 Textures ==
null || Textures.None() ?
Rectangle.Empty :
109 (
int)(Textures.OrderByDescending(t => t.Width).First().Width * spriteSheetZoom),
110 (
int)(Textures.Sum(t => t.Height) * spriteSheetZoom));
112 private const string screenTextTag =
"CharacterEditor.";
120 GUI.ForceMouseOn(
null);
132 CalculateMovementLimits();
133 isEndlessRunner =
true;
144 if (humanSpeciesName.IsEmpty)
164 private void ResetVariables()
166 editCharacterInfo =
false;
168 editAnimations =
false;
172 drawSkeleton =
false;
173 drawDamageModifiers =
false;
174 showParamsEditor =
false;
175 showSpritesheet =
false;
178 limbPairEditing =
false;
179 uniformScaling =
true;
180 lockSpriteOrigin =
true;
181 lockSpritePosition =
false;
182 lockSpriteSize =
false;
183 recalculateCollider =
false;
184 copyJointSettings =
false;
185 showColliders =
false;
186 displayWearables =
true;
187 displayBackgroundColor =
false;
188 jointCreationMode = JointCreationMode.None;
189 isDrawingLimb =
false;
191 cameraOffset = Vector2.Zero;
194 jointStartLimb =
null;
195 visibleSpecies =
null;
196 onlyShowSourceRectForSelectedLimbs =
false;
197 unrestrictSpritesheet =
false;
198 editedCharacters.Clear();
199 selectedJoints.Clear();
200 selectedLimbs.Clear();
201 if (character !=
null)
212 Wizard.instance?.Reset();
215 private void Reset(IEnumerable<Character> characters =
null)
217 characters ??= editedCharacters;
218 characters.ForEach(c => ResetParams(c));
222 private static void ResetParams(Character character)
224 character.Params.Reset(
true);
225 foreach (var animation
in character.AnimController.AllAnimParams)
227 animation.Reset(
true);
228 animation.ClearHistory();
230 character.AnimController.RagdollParams.Reset(
true);
231 character.AnimController.RagdollParams.ClearHistory();
232 character.ForceRun =
false;
233 character.AnimController.ForceSelectAnimationType =
AnimationType.NotDefined;
238 SoundPlayer.OverrideMusicType = Identifier.Empty;
239 GameMain.
SoundManager.SetCategoryGainMultiplier(
"waterambience", GameSettings.CurrentConfig.Audio.SoundVolume, 0);
240 GUI.ForceMouseOn(
null);
245 isEndlessRunner =
false;
247 if (character !=
null && !character.Removed)
267 private void OnResolutionChanged()
274 return TextManager.Get(screenTextTag + tag);
280 if (rightArea ==
null || leftArea ==
null) {
return; }
285 if (displayBackgroundColor)
296 Limb lastLimb = selectedLimbs.LastOrDefault();
297 if (lastLimb ==
null)
299 var lastJoint = selectedJoints.LastOrDefault();
300 if (lastJoint !=
null)
305 if (lastLimb !=
null)
318 if (editLimbs && !unrestrictSpritesheet)
322 if (ShowExtraRagdollControls)
324 createLimbButton.
Enabled = editLimbs;
325 duplicateLimbButton.
Enabled = selectedLimbs.Any();
326 deleteSelectedButton.
Enabled = selectedLimbs.Any() || selectedJoints.Any();
327 createJointButton.
Enabled = selectedLimbs.Any() || selectedJoints.Any();
333 createLimbButton.
Color = Color.Yellow;
338 createLimbButton.
Color = Color.White;
344 switch (jointCreationMode)
346 case JointCreationMode.Select:
347 case JointCreationMode.Create:
349 createJointButton.
Color = Color.Yellow;
353 createJointButton.
Color = Color.White;
358 if (showParamsEditor)
364 public override void Update(
double deltaTime)
366 base.Update(deltaTime);
371 spriteSheetRect = CalculateSpritesheetRectangle();
375 SetToggle(paramsToggle, !paramsToggle.
Selected);
381 if (GUI.KeyboardDispatcher.Subscriber ==
null)
385 SetToggle(characterInfoToggle, !characterInfoToggle.
Selected);
389 SetToggle(ragdollToggle, !ragdollToggle.
Selected);
393 SetToggle(limbsToggle, !limbsToggle.
Selected);
397 SetToggle(jointsToggle, !jointsToggle.
Selected);
401 SetToggle(animsToggle, !animsToggle.
Selected);
406 Widget.EnableMultiSelect = !editAnimations;
410 if (editJoints || editLimbs || editIK)
413 character.AnimController.ResetJoints();
414 character.AnimController.ResetLimbs();
421 CurrentAnimation.
Undo();
428 if (editJoints || editLimbs || editIK)
431 character.AnimController.ResetJoints();
432 character.AnimController.ResetLimbs();
439 CurrentAnimation.
Redo();
447 Widget.EnableMultiSelect =
false;
450 SetToggle(showCollidersToggle, !showCollidersToggle.
Selected);
454 SetToggle(lightsToggle, !lightsToggle.
Selected);
458 SetToggle(damageModifiersToggle, !damageModifiersToggle.
Selected);
462 SetToggle(skeletonToggle, !skeletonToggle.
Selected);
466 SetToggle(spritesheetToggle, !spritesheetToggle.
Selected);
470 SetToggle(ikToggle, !ikToggle.
Selected);
477 character.AnimController.Collider.PhysEnabled =
true;
480 if (animTestPoseToggle.
Enabled)
484 SetToggle(animTestPoseToggle, !animTestPoseToggle.
Selected);
489 animTestPoseToggle.
Selected =
false;
494 bool isSwimming = character.AnimController.ForceSelectAnimationType ==
AnimationType.SwimFast || character.AnimController.ForceSelectAnimationType ==
AnimationType.SwimSlow;
495 bool isMovingFast = character.AnimController.ForceSelectAnimationType ==
AnimationType.Run || character.AnimController.ForceSelectAnimationType ==
AnimationType.SwimFast;
496 if (character.AnimController.CanWalk)
523 index = isMovingFast ? 0 : 1;
528 animSelection.
Select(index);
534 bool isSwimming = character.AnimController.ForceSelectAnimationType ==
AnimationType.SwimFast || character.AnimController.ForceSelectAnimationType ==
AnimationType.SwimSlow;
547 if (selectedLimbs.Any())
549 selectedLimbs.Clear();
552 if (selectedJoints.Any())
554 selectedJoints.Clear();
555 foreach (var w
in jointSelectionWidgets.Values)
558 w.LinkedWidget?.Refresh();
566 jointCreationMode = JointCreationMode.None;
567 isDrawingLimb =
false;
577 ToggleJointCreationMode();
580 UpdateJointCreation();
581 UpdateLimbCreation();
600 holdTimer += deltaTime;
601 if (holdTimer > holdTime)
608 holdTimer += deltaTime;
609 if (holdTimer > holdTime)
616 holdTimer += deltaTime;
617 if (holdTimer > holdTime)
624 holdTimer += deltaTime;
625 if (holdTimer > holdTime)
636 float moveSpeed = (float)deltaTime * 300.0f /
Cam.
Zoom;
643 cameraOffset.Y += moveSpeed;
647 cameraOffset.X -= moveSpeed;
651 cameraOffset.Y -= moveSpeed;
655 cameraOffset.X += moveSpeed;
659 cameraOffset = Vector2.Clamp(cameraOffset, min, max);
674 if (character.IsRagdolled)
676 character.AnimController.ResetPullJoints();
678 character.ControlLocalPlayer((
float)deltaTime,
Cam,
false);
679 character.Control((
float)deltaTime,
Cam);
680 character.AnimController.UpdateAnimations((
float)deltaTime);
681 character.AnimController.UpdateRagdoll((
float)deltaTime,
Cam);
682 character.CurrentHull = character.AnimController.CurrentHull;
685 if (character.Position.X < min)
689 else if (character.Position.X > max)
698 catch (WorldLockedException e)
700 string errorMsg =
"Attempted to modify the state of the physics simulation while a time step was running.";
701 DebugConsole.ThrowError(errorMsg, e);
702 GameAnalyticsManager.AddErrorEventOnce(
"CharacterEditorScreen.Update:WorldLockedException" + e.Message, GameAnalyticsManager.ErrorSeverity.Critical, errorMsg);
706 Cam.
MoveCamera((
float)deltaTime, allowMove:
false, allowZoom: GUI.MouseOn ==
null);
707 Vector2 targetPos = character.WorldPosition;
712 moveSpeed.X = -moveSpeed.X;
713 cameraOffset += moveSpeed;
716 cameraOffset = Vector2.Clamp(cameraOffset, min, max);
721 jointSelectionWidgets.Values.ForEach(w => w.Update((
float)deltaTime));
722 limbEditWidgets.Values.ForEach(w => w.Update((
float)deltaTime));
723 animationWidgets.Values.ForEach(w => w.Update((
float)deltaTime));
727 foreach (
Limb limb
in character.AnimController.Limbs)
729 if (limb ==
null || limb.
ActiveSprite ==
null) {
continue; }
730 if (selectedJoints.Any(j => j.LimbA == limb || j.LimbB == limb)) {
continue; }
734 HandleLimbSelection(limb);
739 HandleLimbSelection(limb);
753 foreach (var limb
in character.AnimController.Limbs)
755 if (limb?.ActiveSprite ==
null) {
continue; }
756 if (selectedJoints.Any(j => j.LimbA == limb || j.LimbB == limb)) {
continue; }
769 private Vector2 scaledMouseSpeed;
770 public override void Draw(
double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
788 base.Draw(deltaTime, graphics, spriteBatch);
790 graphics.Clear(backgroundColor);
793 spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, transformMatrix:
Cam.
Transform);
799 spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, transformMatrix:
Cam.
Transform);
803 character.AnimController.DebugDraw(spriteBatch);
805 else if (showColliders)
807 character.AnimController.Collider.DebugDraw(spriteBatch, Color.White, forceColor:
true);
808 foreach (var limb
in character.AnimController.Limbs)
812 limb.body.DebugDraw(spriteBatch, GUIStyle.Green, forceColor:
true);
822 spriteBatch.Begin(SpriteSortMode.Deferred, Lights.CustomBlendStates.Multiplicative,
null, DepthStencilState.None,
null,
null,
null);
829 if (drawDamageModifiers)
831 foreach (
Limb limb
in character.AnimController.Limbs)
833 if (selectedLimbs.Contains(limb) || selectedLimbs.None())
841 DrawAnimationControls(spriteBatch, (
float)deltaTime);
845 DrawLimbEditor(spriteBatch);
847 if (drawSkeleton || editRagdoll || editJoints || editLimbs || editIK)
849 DrawRagdoll(spriteBatch, (
float)deltaTime);
852 Limb head = character.AnimController.GetLimb(
LimbType.Head);
853 if (head !=
null && character.CanEat && selectedLimbs.Contains(head))
855 var mouthPos = character.AnimController.GetMouthPosition();
856 if (mouthPos.HasValue)
858 ShapeExtensions.DrawPoint(spriteBatch, SimToScreen(mouthPos.Value), GUIStyle.Red, size: 8);
863 DrawSpritesheetEditor(spriteBatch, (
float)deltaTime);
867 GUI.DrawRectangle(spriteBatch, newLimbRect, Color.Yellow);
869 if (jointCreationMode != JointCreationMode.None)
872 if (jointCreationMode == JointCreationMode.Select)
880 if (jointStartLimb !=
null && jointStartLimb.
ActiveSprite !=
null)
882 GUI.DrawRectangle(spriteBatch, GetLimbSpritesheetRect(jointStartLimb), Color.Yellow, thickness: 3);
883 GUI.DrawRectangle(spriteBatch, GetLimbPhysicRect(jointStartLimb), Color.Yellow, thickness: 3);
885 if (jointEndLimb !=
null && jointEndLimb.
ActiveSprite !=
null)
887 GUI.DrawRectangle(spriteBatch, GetLimbSpritesheetRect(jointEndLimb), GUIStyle.Green, thickness: 3);
888 GUI.DrawRectangle(spriteBatch, GetLimbPhysicRect(jointEndLimb), GUIStyle.Green, thickness: 3);
892 if (jointStartLimb !=
null)
894 var startPos = GetLimbSpritesheetRect(jointStartLimb).Center.ToVector2();
895 var offset = anchor1Pos ?? Vector2.Zero;
903 if (jointStartLimb !=
null)
906 var offset = anchor1Pos.HasValue ? Vector2.Transform(anchor1Pos.Value, Matrix.CreateRotationZ(jointStartLimb.
Rotation)) : Vector2.Zero;
919 Vector2 indicatorPos = MiddleWall.Entities.First().DrawPosition;
920 GUI.DrawIndicator(spriteBatch, indicatorPos,
Cam, 700, GUIStyle.SubmarineLocationIcon.Value.Sprite, Color.White);
922 GUI.Draw(
Cam, spriteBatch);
931 if (selectedJoints.Count == 1)
933 GUI.DrawString(spriteBatch,
new Vector2(
GameMain.
GraphicsWidth / 2, 20), $
"{GetCharacterEditorTranslation("Selected")}: {selectedJoints.First().Params.Name}", Color.White, font: GUIStyle.LargeFont);
935 if (selectedLimbs.Count == 1)
937 GUI.DrawString(spriteBatch,
new Vector2(
GameMain.
GraphicsWidth / 2, 20), $
"{GetCharacterEditorTranslation("Selected")}: {selectedLimbs.First().Params.Name}", Color.White, font: GUIStyle.LargeFont);
941 Limb lastLimb = selectedLimbs.LastOrDefault();
942 if (lastLimb ==
null)
944 var lastJoint = selectedJoints.LastOrDefault();
945 if (lastJoint !=
null)
950 if (lastLimb !=
null)
953 bool useSpritesheetOrientation =
float.IsNaN(lastLimb.
Params.SpriteOrientation);
954 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);
956 DrawRadialWidget(spriteBatch,
new Vector2(topLeft.X + 610 * GUI.xScale,
GameMain.
GraphicsHeight - 75 * GUI.yScale), orientation,
960 TryUpdateSubParam(lastLimb.Params,
"spriteorientation".ToIdentifier(), angle);
961 selectedLimbs.ForEach(l => TryUpdateSubParam(l.Params,
"spriteorientation".ToIdentifier(), angle));
964 UpdateOtherLimbs(lastLimb, l => TryUpdateSubParam(l.Params,
"spriteorientation".ToIdentifier(), angle));
966 }, circleRadius: 40, widgetSize: 15, rotationOffset: 0, autoFreeze:
false, rounding: 10);
974 angle => TryUpdateRagdollParam(
"spritesheetorientation", angle), circleRadius: 40, widgetSize: 15, rotationOffset: 0, autoFreeze:
false, rounding: 10);
981 foreach (
Limb limb
in character.AnimController.Limbs)
984 GUI.DrawLine(spriteBatch, limbDrawPos + Vector2.UnitY * 5.0f, limbDrawPos - Vector2.UnitY * 5.0f, Color.White);
985 GUI.DrawLine(spriteBatch, limbDrawPos + Vector2.UnitX * 5.0f, limbDrawPos - Vector2.UnitX * 5.0f, Color.White);
988 GUI.DrawString(spriteBatch,
new Vector2(
GameMain.
GraphicsWidth / 2, 0), $
"Cursor World Pos: {character.CursorWorldPosition}", Color.White, font: GUIStyle.SmallFont);
989 GUI.DrawString(spriteBatch,
new Vector2(
GameMain.
GraphicsWidth / 2, 20), $
"Cursor Pos: {character.CursorPosition}", Color.White, font: GUIStyle.SmallFont);
990 GUI.DrawString(spriteBatch,
new Vector2(
GameMain.
GraphicsWidth / 2, 40), $
"Cursor Screen Pos: {PlayerInput.MousePosition}", Color.White, font: GUIStyle.SmallFont);
993 var collider = character.AnimController.Collider;
994 var colliderDrawPos = SimToScreen(collider.SimPosition);
995 Vector2 forward = Vector2.Transform(Vector2.UnitY, Matrix.CreateRotationZ(collider.Rotation));
996 var endPos = SimToScreen(collider.SimPosition + forward * collider.Radius);
997 GUI.DrawLine(spriteBatch, colliderDrawPos, endPos, GUIStyle.Green);
998 GUI.DrawLine(spriteBatch, colliderDrawPos, SimToScreen(collider.SimPosition + forward * 0.25f), Color.Blue);
999 Vector2 left = forward.Left();
1000 GUI.DrawLine(spriteBatch, colliderDrawPos, SimToScreen(collider.SimPosition + left * 0.25f), GUIStyle.Red);
1001 ShapeExtensions.DrawCircle(spriteBatch, colliderDrawPos, (endPos - colliderDrawPos).Length(), 40, GUIStyle.Green);
1002 GUI.DrawString(spriteBatch,
new Vector2(
GameMain.
GraphicsWidth - 300, 0), $
"Collider rotation: {MathHelper.ToDegrees(MathUtils.WrapAngleTwoPi(collider.Rotation))}", Color.White, font: GUIStyle.SmallFont);
1008 #region Ragdoll Manipulation
1009 private void UpdateJointCreation()
1011 if (jointCreationMode == JointCreationMode.None)
1013 jointStartLimb =
null;
1014 jointEndLimb =
null;
1020 var selectedJoint = selectedJoints.LastOrDefault();
1021 if (selectedJoint !=
null)
1023 if (jointCreationMode == JointCreationMode.Create)
1025 if (spriteSheetRect.Contains(PlayerInput.MousePosition))
1027 jointEndLimb = GetClosestLimbOnSpritesheet(PlayerInput.MousePosition, l => l !=
null && l != jointStartLimb && l.ActiveSprite !=
null);
1028 if (jointEndLimb !=
null && PlayerInput.PrimaryMouseButtonClicked())
1030 Vector2 anchor1 = anchor1Pos.HasValue ? anchor1Pos.Value / spriteSheetZoom : Vector2.Zero;
1031 anchor1.X = -anchor1.X;
1032 Vector2 anchor2 = (GetLimbSpritesheetRect(jointEndLimb).Center.ToVector2() - PlayerInput.MousePosition) / spriteSheetZoom;
1033 anchor2.X = -anchor2.X;
1034 CreateJoint(jointStartLimb.
Params.ID, jointEndLimb.
Params.ID, anchor1, anchor2);
1035 jointCreationMode = JointCreationMode.None;
1040 jointEndLimb = GetClosestLimbOnRagdoll(PlayerInput.MousePosition, l => l !=
null && l != jointStartLimb && l.ActiveSprite !=
null);
1041 if (jointEndLimb !=
null && PlayerInput.PrimaryMouseButtonClicked())
1043 Vector2 anchor2 = ConvertUnits.ToDisplayUnits(jointEndLimb.
body.
FarseerBody.GetLocalPoint(ScreenToSim(PlayerInput.MousePosition)));
1044 CreateJoint(jointStartLimb.
Params.ID, jointEndLimb.
Params.ID, anchor1Pos, anchor2);
1045 jointCreationMode = JointCreationMode.None;
1051 jointStartLimb = selectedJoint.LimbB;
1052 if (spriteSheetRect.Contains(PlayerInput.MousePosition))
1054 anchor1Pos = GetLimbSpritesheetRect(jointStartLimb).Center.ToVector2() - PlayerInput.MousePosition;
1058 anchor1Pos = ConvertUnits.ToDisplayUnits(jointStartLimb.
body.
FarseerBody.GetLocalPoint(ScreenToSim(PlayerInput.MousePosition)));
1060 if (PlayerInput.PrimaryMouseButtonClicked())
1062 jointCreationMode = JointCreationMode.Create;
1068 jointCreationMode = JointCreationMode.None;
1073 if (selectedLimbs.Any())
1075 if (spriteSheetRect.Contains(PlayerInput.MousePosition))
1077 if (jointCreationMode == JointCreationMode.Create)
1079 jointEndLimb = GetClosestLimbOnSpritesheet(PlayerInput.MousePosition, l => l !=
null && l != jointStartLimb && l.ActiveSprite !=
null && !l.Hidden);
1080 if (jointEndLimb !=
null && PlayerInput.PrimaryMouseButtonClicked())
1082 Vector2 anchor1 = anchor1Pos.HasValue ? anchor1Pos.Value / spriteSheetZoom : Vector2.Zero;
1083 anchor1.X = -anchor1.X;
1084 Vector2 anchor2 = (GetLimbSpritesheetRect(jointEndLimb).Center.ToVector2() - PlayerInput.MousePosition) / spriteSheetZoom;
1085 anchor2.X = -anchor2.X;
1086 CreateJoint(jointStartLimb.
Params.ID, jointEndLimb.
Params.ID, anchor1, anchor2);
1087 jointCreationMode = JointCreationMode.None;
1090 else if (PlayerInput.PrimaryMouseButtonClicked())
1092 jointStartLimb = GetClosestLimbOnSpritesheet(PlayerInput.MousePosition, l => selectedLimbs.Contains(l) && !l.Hidden);
1093 anchor1Pos = GetLimbSpritesheetRect(jointStartLimb).Center.ToVector2() - PlayerInput.MousePosition;
1094 jointCreationMode = JointCreationMode.Create;
1099 if (jointCreationMode == JointCreationMode.Create)
1101 jointEndLimb = GetClosestLimbOnRagdoll(PlayerInput.MousePosition, l => l !=
null && l != jointStartLimb && l.ActiveSprite !=
null && !l.Hidden);
1102 if (jointEndLimb !=
null && PlayerInput.PrimaryMouseButtonClicked())
1104 Vector2 anchor1 = anchor1Pos ?? Vector2.Zero;
1105 Vector2 anchor2 = ConvertUnits.ToDisplayUnits(jointEndLimb.
body.
FarseerBody.GetLocalPoint(ScreenToSim(PlayerInput.MousePosition)));
1106 CreateJoint(jointStartLimb.
Params.ID, jointEndLimb.
Params.ID, anchor1, anchor2);
1107 jointCreationMode = JointCreationMode.None;
1110 else if (PlayerInput.PrimaryMouseButtonClicked())
1112 jointStartLimb = GetClosestLimbOnRagdoll(PlayerInput.MousePosition, l => selectedLimbs.Contains(l) && !l.Hidden);
1113 anchor1Pos = ConvertUnits.ToDisplayUnits(jointStartLimb.
body.
FarseerBody.GetLocalPoint(ScreenToSim(PlayerInput.MousePosition)));
1114 jointCreationMode = JointCreationMode.Create;
1120 jointCreationMode = JointCreationMode.None;
1125 private void UpdateLimbCreation()
1134 SetToggle(limbsToggle,
true);
1136 if (PlayerInput.PrimaryMouseButtonHeld())
1140 newLimbRect =
new Rectangle((
int)PlayerInput.MousePosition.X, (
int)PlayerInput.MousePosition.Y, 0, 0);
1144 newLimbRect.Size =
new Point((
int)PlayerInput.MousePosition.X - newLimbRect.X, (
int)PlayerInput.MousePosition.Y - newLimbRect.Y);
1146 newLimbRect.Size =
new Point(Math.Max(newLimbRect.Width, 2), Math.Max(newLimbRect.Height, 2));
1148 if (PlayerInput.PrimaryMouseButtonClicked())
1151 newLimbRect.Location =
new Point(newLimbRect.X - spriteSheetOffsetX, newLimbRect.Y - spriteSheetOffsetY);
1152 newLimbRect = newLimbRect.Divide(spriteSheetZoom);
1153 CreateNewLimb(newLimbRect);
1154 isDrawingLimb =
false;
1159 private void CopyLimb(Limb limb)
1161 if (limb ==
null) {
return; }
1163 var rect = limb.ActiveSprite.SourceRect;
1164 var spriteParams = limb.Params.GetSprite();
1165 var newLimbElement =
new XElement(
"limb",
1166 new XAttribute(
"id", RagdollParams.Limbs.Last().ID + 1),
1167 new XAttribute(
"radius", limb.Params.Radius),
1168 new XAttribute(
"width", limb.Params.Width),
1169 new XAttribute(
"height", limb.Params.Height),
1170 new XElement(
"sprite",
1171 new XAttribute(
"texture", spriteParams.Texture),
1172 new XAttribute(
"sourcerect", $
"{rect.X}, {rect.Y}, {rect.Size.X}, {rect.Size.Y}"))).FromPackage(character.Prefab.ContentPackage);
1173 CreateLimb(newLimbElement);
1176 private void CreateNewLimb(Rectangle sourceRect)
1178 var newLimbElement =
new XElement(
"limb",
1179 new XAttribute(
"id", RagdollParams.Limbs.Last().ID + 1),
1180 new XAttribute(
"width", sourceRect.Width * RagdollParams.TextureScale),
1181 new XAttribute(
"height", sourceRect.Height * RagdollParams.TextureScale),
1182 new XElement(
"sprite",
1183 new XAttribute(
"texture", RagdollParams.Limbs.First().GetSprite().Texture),
1184 new XAttribute(
"sourcerect", $
"{sourceRect.X}, {sourceRect.Y}, {sourceRect.Width}, {sourceRect.Height}"))).FromPackage(character.Prefab.ContentPackage);
1185 CreateLimb(newLimbElement);
1186 lockSpriteOriginToggle.Selected =
false;
1187 recalculateColliderToggle.Selected =
true;
1190 private void CreateLimb(ContentXElement newElement)
1192 if (RagdollParams.MainElement ==
null)
1194 DebugConsole.ThrowError(
"Main element null! Failed to create a limb.");
1197 var lastElement = RagdollParams.MainElement.GetChildElements(
"limb").LastOrDefault();
1198 if (lastElement !=
null)
1200 lastElement.AddAfterSelf(newElement);
1204 RagdollParams.MainElement.AddFirst(newElement);
1206 var newLimbParams =
new RagdollParams.LimbParams(newElement, RagdollParams);
1207 RagdollParams.Limbs.Add(newLimbParams);
1208 character.AnimController.Recreate();
1210 TeleportTo(spawnPosition);
1213 selectedLimbs.Add(character.AnimController.Limbs.Single(l => l.Params == newLimbParams));
1214 ResetParamsEditor();
1220 private void CreateJoint(
int fromLimb,
int toLimb, Vector2? anchor1 =
null, Vector2? anchor2 =
null)
1222 if (RagdollParams.Joints.Any(j => j.Limb1 == fromLimb && j.Limb2 == toLimb))
1224 DebugConsole.ThrowErrorLocalized(GetCharacterEditorTranslation(
"ExistingJointFound").Replace(
"[limbid1]", fromLimb.ToString()).Replace(
"[limbid2]", toLimb.ToString()));
1227 if (RagdollParams.MainElement ==
null)
1229 DebugConsole.ThrowError(
"The main element of the ragdoll params is null! Failed to create a joint.");
1233 Vector2 a1 = anchor1 ?? Vector2.Zero;
1234 Vector2 a2 = anchor2 ?? Vector2.Zero;
1235 var newJointElement =
new XElement(
"joint",
1236 new XAttribute(
"limb1", fromLimb),
1237 new XAttribute(
"limb2", toLimb),
1238 new XAttribute(
"limb1anchor", $
"{a1.X.Format(2)}, {a1.Y.Format(2)}"),
1239 new XAttribute(
"limb2anchor", $
"{a2.X.Format(2)}, {a2.Y.Format(2)}")
1240 ).FromPackage(character.Prefab.ContentPackage);
1241 var lastJointElement = RagdollParams.MainElement.GetChildElements(
"joint").LastOrDefault() ?? RagdollParams.MainElement.GetChildElements(
"limb").LastOrDefault();
1242 if (lastJointElement ==
null)
1244 DebugConsole.ThrowErrorLocalized(GetCharacterEditorTranslation(
"CantAddJointsNoLimbElements"));
1247 lastJointElement.AddAfterSelf(newJointElement);
1248 var newJointParams =
new RagdollParams.JointParams(newJointElement, RagdollParams);
1249 RagdollParams.Joints.Add(newJointParams);
1250 character.AnimController.Recreate();
1252 TeleportTo(spawnPosition);
1255 SetToggle(jointsToggle,
true);
1256 selectedJoints.Add(character.AnimController.LimbJoints.Single(j => j.Params == newJointParams));
1262 private void DeleteSelected()
1265 for (
int i = 0; i < selectedJoints.Count; i++)
1267 var joint = selectedJoints[i];
1268 joint.Params.Element.Remove();
1269 RagdollParams.Joints.Remove(joint.Params);
1271 var removedIDs =
new List<int>();
1272 for (
int i = 0; i < selectedLimbs.Count; i++)
1274 if (character.IsHumanoid)
1276 DebugConsole.ThrowErrorLocalized(GetCharacterEditorTranslation(
"HumanoidLimbDeletionDisabled"));
1279 var limb = selectedLimbs[i];
1280 if (limb == character.AnimController.MainLimb)
1282 DebugConsole.ThrowError(
"Can't remove the main limb, because it will cause unreveratable issues.");
1285 removedIDs.Add(limb.Params.ID);
1286 limb.Params.Element.Remove();
1287 RagdollParams.Limbs.Remove(limb.Params);
1290 var renamedIDs =
new Dictionary<int, int>();
1291 for (
int i = 0; i < RagdollParams.Limbs.Count; i++)
1293 int oldID = RagdollParams.Limbs[i].ID;
1297 var limbParams = RagdollParams.Limbs[i];
1298 limbParams.ID = newID;
1299 limbParams.Name = limbParams.GenerateName();
1300 renamedIDs.Add(oldID, newID);
1304 var jointsToRemove =
new List<RagdollParams.JointParams>();
1305 for (
int i = 0; i < RagdollParams.Joints.Count; i++)
1307 var joint = RagdollParams.Joints[i];
1308 if (removedIDs.Contains(joint.Limb1) || removedIDs.Contains(joint.Limb2))
1311 jointsToRemove.Add(joint);
1316 bool rename =
false;
1317 if (renamedIDs.TryGetValue(joint.Limb1, out
int newID1))
1319 joint.Limb1 = newID1;
1322 if (renamedIDs.TryGetValue(joint.Limb2, out
int newID2))
1324 joint.Limb2 = newID2;
1329 joint.Name = joint.GenerateName();
1333 foreach (var jointParam
in jointsToRemove)
1335 jointParam.Element.Remove();
1336 RagdollParams.Joints.Remove(jointParam);
1342 #region Endless runner
1345 private void CalculateMovementLimits()
1347 min = MiddleWall.Entities.Select(w => w.Rect.Left).OrderBy(p => p).First();
1348 max = MiddleWall.Entities.Select(w => w.Rect.Right).OrderBy(p => p).Last();
1351 private readonly WallGroup[] wallGroups =
new WallGroup[3];
1353 private WallGroup MiddleWall => wallGroups[1];
1355 private IEnumerable<MapEntity> AllStructures => wallGroups.SelectMany(c => c.Entities);
1357 private class WallGroup
1359 public readonly List<MapEntity> Entities;
1361 public WallGroup(List<MapEntity> entities)
1363 Entities = entities;
1366 public WallGroup Clone()
1368 var clones =
new List<MapEntity>();
1369 Entities.ForEachMod(w => clones.Add(w.Clone()));
1370 return new WallGroup(clones);
1374 private void CloneWalls()
1376 var originalWall = wallGroups[0];
1377 int moveAmount = originalWall.Entities.FirstOrDefault(e => e is Structure).Rect.Width;
1378 for (
int i = 1; i <= 2; i++)
1380 wallGroups[i] = originalWall.Clone();
1381 foreach (var entity
in wallGroups[i].Entities)
1383 entity.Move(
new Vector2(moveAmount * i, 0));
1388 private void UpdateWalls(
bool right)
1390 int moveAmount = wallGroups[0].Entities.FirstOrDefault(e => e is Structure).Rect.Width;
1391 int amount = right ? moveAmount : -moveAmount;
1392 foreach (var wallGroup
in wallGroups)
1394 foreach (var entity
in wallGroup.Entities)
1396 entity.Move(
new Vector2(amount, 0));
1400 CalculateMovementLimits();
1402 GameMain.World.ProcessChanges();
1405 private bool wallCollisionsEnabled;
1406 private void SetWallCollisions(
bool enabled)
1408 if (!isEndlessRunner) {
return; }
1409 wallCollisionsEnabled = enabled;
1410 var collisionCategory = enabled ? FarseerPhysics.Dynamics.Category.Cat1 : FarseerPhysics.Dynamics.Category.None;
1411 AllStructures.ForEach(w => (w as Structure)?.SetCollisionCategory(collisionCategory));
1412 GameMain.World.ProcessChanges();
1416 #region Character spawning
1417 private int characterIndex = -1;
1418 private Identifier currentCharacterIdentifier;
1419 private Identifier selectedJob = Identifier.Empty;
1421 private List<Identifier> visibleSpecies;
1422 private List<Identifier> VisibleSpecies
1426 visibleSpecies ??= CharacterPrefab.Prefabs.Where(ShowCreature).OrderBy(p => p.Identifier).Select(p => p.Identifier).ToList();
1427 return visibleSpecies;
1431 private bool ShowCreature(CharacterPrefab prefab)
1433 Identifier speciesName = prefab.Identifier;
1434 if (speciesName == CharacterPrefab.HumanSpeciesName) {
return true; }
1435 if (!VanillaCharacters.Contains(prefab.ContentFile))
1440 if (CreatureMetrics.UnlockAll) {
return true; }
1441 return CreatureMetrics.Unlocked.Contains(speciesName);
1444 private IEnumerable<CharacterFile> vanillaCharacters;
1445 private IEnumerable<CharacterFile> VanillaCharacters
1449 vanillaCharacters ??= GameMain.VanillaContent.GetFiles<CharacterFile>();
1450 return vanillaCharacters;
1454 private Identifier GetNextCharacterIdentifier()
1456 GetCurrentCharacterIndex();
1458 currentCharacterIdentifier = VisibleSpecies[characterIndex];
1459 return currentCharacterIdentifier;
1462 private Identifier GetPreviousCharacterIdentifier()
1464 GetCurrentCharacterIndex();
1466 currentCharacterIdentifier = VisibleSpecies[characterIndex];
1467 return currentCharacterIdentifier;
1470 private void GetCurrentCharacterIndex()
1472 characterIndex = VisibleSpecies.IndexOf(character.SpeciesName);
1475 private void IncreaseIndex()
1478 if (characterIndex > VisibleSpecies.Count - 1)
1484 private void ReduceIndex()
1487 if (characterIndex < 0)
1489 characterIndex = VisibleSpecies.Count - 1;
1495 DebugConsole.NewMessage(GetCharacterEditorTranslation(
"TryingToSpawnCharacter").Replace(
"[config]", speciesName.ToString()), Color.HotPink);
1497 bool dontFollowCursor =
true;
1498 if (character !=
null)
1500 dontFollowCursor = character.dontFollowCursor;
1502 CurrentAnimation.ClearHistory();
1503 if (!character.Removed)
1512 character =
Character.
Create(speciesName, spawnPosition, ToolBox.RandomSeed(8), characterInfo, hasAi:
false, ragdoll: ragdoll);
1515 if (displayWearables)
1519 selectedJob = characterInfo.Job.Prefab.Identifier;
1523 character =
Character.
Create(speciesName, spawnPosition, ToolBox.RandomSeed(8), hasAi:
false, ragdoll: ragdoll);
1524 selectedJob = Identifier.Empty;
1526 if (character !=
null)
1530 if (character ==
null)
1532 if (currentCharacterIdentifier == speciesName)
1539 SpawnCharacter(currentCharacterIdentifier);
1546 private void OnPreSpawn()
1548 cameraOffset = Vector2.Zero;
1550 if (!isEndlessRunner)
1558 private void OnPostSpawn()
1560 currentCharacterIdentifier = character.SpeciesName;
1561 GetCurrentCharacterIndex();
1562 character.Submarine = Submarine.MainSub;
1563 character.AnimController.forceStanding = character.AnimController.CanWalk;
1564 character.AnimController.ForceSelectAnimationType = character.AnimController.CanWalk ?
AnimationType.Walk :
AnimationType.SwimSlow;
1565 Character.Controlled = character;
1566 SetWallCollisions(character.AnimController.forceStanding);
1571 ResetParamsEditor();
1572 CurrentAnimation.StoreSnapshot();
1573 RagdollParams.StoreSnapshot();
1574 Cam.Position = character.WorldPosition;
1575 editedCharacters.Add(character);
1578 private void ClearWidgets()
1580 Widget.SelectedWidgets.Clear();
1581 animationWidgets.Clear();
1582 jointSelectionWidgets.Clear();
1583 limbEditWidgets.Clear();
1586 private void ClearSelection()
1588 selectedLimbs.Clear();
1589 selectedJoints.Clear();
1590 foreach (var w
in jointSelectionWidgets.Values)
1593 w.LinkedWidget?.Refresh();
1597 private void RecreateRagdoll(RagdollParams ragdoll =
null)
1599 RagdollParams.Apply();
1600 character.AnimController.Recreate(ragdoll);
1601 TeleportTo(spawnPosition);
1603 var selectedJointParams = selectedJoints.Select(j => j.Params).ToList();
1604 var selectedLimbParams = selectedLimbs.Select(l => l.Params).ToList();
1608 foreach (var joint
in character.AnimController.LimbJoints)
1610 if (selectedJointParams.Contains(joint.Params))
1612 selectedJoints.Add(joint);
1615 foreach (var limb
in character.AnimController.Limbs)
1617 if (selectedLimbParams.Contains(limb.Params))
1619 selectedLimbs.Add(limb);
1622 ResetParamsEditor();
1625 private void TeleportTo(Vector2 position)
1627 if (isEndlessRunner)
1629 character.AnimController.SetPosition(ConvertUnits.ToSimUnits(position),
false);
1633 character.TeleportTo(position);
1635 Cam.Position = character.WorldPosition;
1638 public bool CreateCharacter(Identifier name,
string mainFolder,
bool isHumanoid,
ContentPackage contentPackage, XElement ragdoll, XElement config =
null, IEnumerable<AnimationParams> animations =
null)
1642 throw new ArgumentException(
"Name cannot be empty.");
1647 if (contentPackage ==
null)
1649 contentPackage = ContentPackageManager.EnabledPackages.All.LastOrDefault(cp => cp != vanilla);
1651 if (contentPackage ==
null)
1654 DebugConsole.ThrowErrorLocalized(GetCharacterEditorTranslation(
"NoContentPackageSelected"));
1657 if (vanilla !=
null && contentPackage == vanilla)
1659 GUI.AddMessage(GetCharacterEditorTranslation(
"CannotEditVanillaCharacters"), GUIStyle.Red, font: GUIStyle.LargeFont);
1663 if (contentPackage is
RegularPackage regular && !ContentPackageManager.EnabledPackages.Regular.Contains(regular))
1665 ContentPackageManager.EnabledPackages.EnableRegular(regular);
1667 GameSettings.SaveCurrentConfig();
1670 string configFilePath = Path.Combine(mainFolder, $
"{name}.xml").Replace(
@"\",
@"/");
1672 XElement overrideElement =
null;
1673 if (duplicate !=
null)
1675 visibleSpecies =
null;
1676 if (!File.Exists(configFilePath))
1680 overrideElement =
new XElement(
"override");
1686 config =
new XElement(
"Character",
1687 new XAttribute(
"speciesname", name),
1688 new XAttribute(
"humanoid", isHumanoid),
1689 new XElement(
"ragdolls", CreateRagdollPath()),
1690 new XElement(
"animations", CreateAnimationPath()),
1691 new XElement(
"health"),
1692 new XElement(
"ai"));
1696 config.SetAttributeValue(
"speciesname", name, StringComparison.OrdinalIgnoreCase);
1697 config.SetAttributeValue(
"humanoid", isHumanoid, StringComparison.OrdinalIgnoreCase);
1698 var ragdollElement = config.GetChildElement(
"ragdolls");
1699 if (ragdollElement ==
null)
1701 config.Add(
new XElement(
"ragdolls", CreateRagdollPath()));
1705 var path = ragdollElement.GetAttributeString(
"folder",
"");
1706 if (!
string.IsNullOrEmpty(path) && !path.Equals(
"default", StringComparison.OrdinalIgnoreCase))
1708 ragdollElement.ReplaceWith(
new XElement(
"ragdolls", CreateRagdollPath()));
1711 var animationElement = config.GetChildElement(
"animations");
1712 if (animationElement ==
null)
1714 config.Add(
new XElement(
"animations", CreateAnimationPath()));
1718 var path = animationElement.GetAttributeString(
"folder",
"");
1719 if (!
string.IsNullOrEmpty(path) && !path.Equals(
"default", StringComparison.OrdinalIgnoreCase))
1721 animationElement.ReplaceWith(
new XElement(
"animations", CreateAnimationPath()));
1726 XAttribute CreateRagdollPath() =>
new XAttribute(
"folder", Path.Combine(mainFolder, $
"Ragdolls/").Replace(
@"\",
@"/"));
1727 XAttribute CreateAnimationPath() =>
new XAttribute(
"folder", Path.Combine(mainFolder, $
"Animations/").Replace(
@"\",
@"/"));
1729 if (overrideElement !=
null)
1731 overrideElement.Add(config);
1732 config = overrideElement;
1734 XDocument doc =
new XDocument(config);
1737 Directory.CreateDirectory(Path.GetDirectoryName(configFileContentPath.
Value));
1739 doc.Save(configFileContentPath.
Value);
1741 doc.SaveSafe(configFileContentPath.
Value);
1744 var modProject =
new ModProject(contentPackage);
1746 modProject.AddFile(newFile);
1747 modProject.Save(contentPackage.
Path);
1749 var reloadResult = ContentPackageManager.ReloadContentPackage(contentPackage);
1750 if (!reloadResult.TryUnwrapSuccess(out var newPackage))
1752 throw new Exception($
"Failed to reload package",
1753 reloadResult.TryUnwrapFailure(out var exception) ? exception :
null);
1755 contentPackage = newPackage;
1757 DebugConsole.NewMessage(GetCharacterEditorTranslation(
"ContentPackageSaved").Replace(
"[path]", contentPackage.
Path));
1769 if (animations !=
null)
1771 if (!Directory.Exists(animFolder))
1773 Directory.CreateDirectory(animFolder);
1775 foreach (var animation
in animations)
1777 XElement element = animation.MainElement;
1778 if (element ==
null) {
continue; }
1779 element.SetAttributeValue(
"type", name);
1783 element.Save(fullPath);
1785 element.SaveSafe(fullPath);
1797 if (!ragdollParams.
CanWalk) {
continue; }
1800 if (!ragdollParams.
CanWalk || !isHumanoid) {
continue; }
1812 if (!VisibleSpecies.Contains(name))
1814 VisibleSpecies.Add(name);
1816 SpawnCharacter(name, ragdollParams);
1817 limbPairEditing =
false;
1818 limbsToggle.Selected =
true;
1819 recalculateColliderToggle.Selected =
true;
1820 lockSpriteOriginToggle.Selected =
false;
1821 selectedLimbs.Add(character.AnimController.Limbs.First());
1825 private void ShowWearables()
1827 if (character.Inventory ==
null) {
return; }
1828 foreach (var item
in character.Inventory.AllItems)
1831 if (item.AllowedSlots.Contains(
InvSlotType.Head) || item.AllowedSlots.Contains(
InvSlotType.Headset)) {
continue; }
1832 item.Equip(character);
1836 private void HideWearables()
1838 character.Inventory?.AllItemsMod.ForEach(i => i.Unequip(character));
1843 private static Vector2 innerScale =
new Vector2(0.95f, 0.95f);
1845 private GUILayoutGroup rightArea, leftArea;
1846 private GUIFrame centerArea;
1848 private GUIFrame characterSelectionPanel;
1849 private GUIFrame fileEditPanel;
1850 private GUIFrame modesPanel;
1851 private GUIFrame buttonsPanel;
1852 private GUIFrame optionsPanel;
1853 private GUIFrame minorModesPanel;
1855 private GUIFrame ragdollControls;
1856 private GUIFrame jointControls;
1857 private GUIFrame animationControls;
1858 private GUIFrame limbControls;
1859 private GUIFrame spriteSheetControls;
1860 private GUIFrame backgroundColorPanel;
1862 private GUIDropDown animSelection;
1863 private GUITickBox freezeToggle;
1864 private GUITickBox animTestPoseToggle;
1865 private GUITickBox showCollidersToggle;
1866 private GUIScrollBar jointScaleBar;
1867 private GUIScrollBar limbScaleBar;
1868 private GUIScrollBar spriteSheetZoomBar;
1869 private GUITickBox copyJointsToggle;
1870 private GUITickBox recalculateColliderToggle;
1871 private GUIFrame resetSpriteOrientationButtonParent;
1873 private GUITickBox characterInfoToggle;
1874 private GUITickBox ragdollToggle;
1875 private GUITickBox animsToggle;
1876 private GUITickBox limbsToggle;
1877 private GUITickBox paramsToggle;
1878 private GUITickBox jointsToggle;
1879 private GUITickBox spritesheetToggle;
1880 private GUITickBox skeletonToggle;
1881 private GUITickBox lightsToggle;
1882 private GUITickBox damageModifiersToggle;
1883 private GUITickBox ikToggle;
1884 private GUITickBox lockSpriteOriginToggle;
1886 private GUIFrame extraRagdollControls;
1887 private GUIButton createJointButton;
1888 private GUIButton createLimbButton;
1889 private GUIButton deleteSelectedButton;
1890 private GUIButton duplicateLimbButton;
1892 private ToggleButton modesToggle;
1893 private ToggleButton minorModesToggle;
1894 private ToggleButton buttonsPanelToggle;
1895 private ToggleButton optionsToggle;
1896 private ToggleButton characterPanelToggle;
1897 private ToggleButton fileEditToggle;
1899 private void CreateGUI()
1902 if (rightArea !=
null)
1904 rightArea.RectTransform.Parent =
null;
1906 if (centerArea !=
null)
1908 centerArea.RectTransform.Parent =
null;
1910 if (leftArea !=
null)
1912 leftArea.RectTransform.Parent =
null;
1916 rightArea =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.15f, 1.0f), parent: Frame.RectTransform, anchor:
Anchor.CenterRight), childAnchor:
Anchor.BottomRight)
1918 RelativeSpacing = 0.02f
1920 centerArea =
new GUIFrame(
new RectTransform(
new Vector2(0.5f, 0.95f), parent: Frame.RectTransform, anchor:
Anchor.TopRight)
1922 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))
1925 { CanBeFocused =
false };
1926 leftArea =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.15f, 0.95f), parent: Frame.RectTransform, anchor:
Anchor.CenterLeft), childAnchor:
Anchor.BottomLeft)
1928 RelativeSpacing = 0.02f
1931 Vector2 toggleSize =
new Vector2(1.0f, 0.03f);
1933 CreateFileEditPanel();
1934 CreateOptionsPanel(toggleSize);
1935 CreateCharacterSelectionPanel();
1936 if (rightArea.RectTransform.Children.Sum(c => c.Rect.Height) > GameMain.GraphicsHeight)
1938 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)));
1939 fileEditPanel.RectTransform.MinSize =
new Point(0, (
int)(fileEditPanel.GetChild<GUILayoutGroup>().RectTransform.Children.Sum(c => c.Rect.Height) / innerScale.Y));
1940 optionsPanel.GetAllChildren().Where(c => c is GUITickBox).ForEach(t => t.RectTransform.MinSize = t.RectTransform.MinSize.Multiply(
new Vector2(1.0f, 0.75f)));
1941 optionsPanel.RectTransform.MinSize =
new Point(0, (
int)(optionsPanel.GetChild<GUILayoutGroup>().RectTransform.Children.Sum(c => c.Rect.Height) / innerScale.Y));
1942 rightArea.Recalculate();
1945 CreateButtonsPanel();
1946 CreateModesPanel(toggleSize);
1947 CreateMinorModesPanel(toggleSize);
1949 CreateContextualControls();
1952 private void CreateMinorModesPanel(Vector2 toggleSize)
1954 minorModesPanel =
new GUIFrame(
new RectTransform(
new Vector2(1.0f, 0.25f), leftArea.RectTransform));
1955 var layoutGroup =
new GUILayoutGroup(
new RectTransform(innerScale, minorModesPanel.RectTransform,
Anchor.Center))
1957 AbsoluteSpacing = 2,
1960 new GUITextBlock(
new RectTransform(
new Vector2(0.03f, 0.0f), layoutGroup.RectTransform), GetCharacterEditorTranslation(
"MinorModesTitle"), font: GUIStyle.LargeFont);
1961 paramsToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"ShowParameters")) {
Selected = showParamsEditor };
1962 paramsToggle.OnSelected = box =>
1964 showParamsEditor = box.Selected;
1967 spritesheetToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"ShowSpriteSheet")) {
Selected = showSpritesheet };
1968 spritesheetToggle.OnSelected = box =>
1970 showSpritesheet = box.Selected;
1973 showCollidersToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"ShowColliders"))
1978 showColliders = box.Selected;
1982 ikToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"EditIKTargets")) {
Selected = editIK };
1983 ikToggle.OnSelected = box =>
1985 editIK = box.Selected;
1988 skeletonToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"DrawSkeleton")) {
Selected = drawSkeleton };
1989 skeletonToggle.OnSelected = box =>
1991 drawSkeleton = box.Selected;
1994 lightsToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"EnableLights")) {
Selected = GameMain.LightManager.LightingEnabled };
1995 lightsToggle.OnSelected = box =>
1997 GameMain.LightManager.LightingEnabled = box.Selected;
2000 damageModifiersToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"DrawDamageModifiers")) {
Selected = drawDamageModifiers };
2001 damageModifiersToggle.OnSelected = box =>
2003 drawDamageModifiers = box.Selected;
2006 minorModesToggle =
new ToggleButton(
new RectTransform(
new Vector2(0.08f, 1), minorModesPanel.RectTransform,
Anchor.CenterRight,
Pivot.CenterLeft),
Direction.Left);
2007 minorModesPanel.RectTransform.MinSize =
new Point(0, (
int)(layoutGroup.RectTransform.Children.Sum(c => c.MinSize.Y + layoutGroup.AbsoluteSpacing) * 1.2f));
2010 private void CreateModesPanel(Vector2 toggleSize)
2012 modesPanel =
new GUIFrame(
new RectTransform(
new Vector2(1.0f, 0.2f), leftArea.RectTransform));
2013 var layoutGroup =
new GUILayoutGroup(
new RectTransform(innerScale, modesPanel.RectTransform,
Anchor.Center))
2015 AbsoluteSpacing = 2,
2018 new GUITextBlock(
new RectTransform(
new Vector2(0.03f, 0.0f), layoutGroup.RectTransform), GetCharacterEditorTranslation(
"ModesPanel"), font: GUIStyle.LargeFont);
2019 characterInfoToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"EditCharacter")) {
Selected = editCharacterInfo };
2020 ragdollToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"EditRagdoll")) {
Selected = editRagdoll };
2021 limbsToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"EditLimbs")) {
Selected = editLimbs };
2022 jointsToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"EditJoints")) {
Selected = editJoints };
2023 animsToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"EditAnimations")) {
Selected = editAnimations };
2024 animsToggle.OnSelected = box =>
2026 editAnimations = box.Selected;
2029 SetToggle(limbsToggle,
false);
2030 SetToggle(jointsToggle,
false);
2031 SetToggle(ragdollToggle,
false);
2032 SetToggle(characterInfoToggle,
false);
2033 spritesheetToggle.Selected =
false;
2036 ResetParamsEditor();
2039 limbsToggle.OnSelected = box =>
2041 editLimbs = box.Selected;
2044 SetToggle(animsToggle,
false);
2045 SetToggle(jointsToggle,
false);
2046 SetToggle(ragdollToggle,
false);
2047 SetToggle(characterInfoToggle,
false);
2048 spritesheetToggle.Selected =
true;
2051 ResetParamsEditor();
2054 jointsToggle.OnSelected = box =>
2056 editJoints = box.Selected;
2059 SetToggle(limbsToggle,
false);
2060 SetToggle(animsToggle,
false);
2061 SetToggle(ragdollToggle,
false);
2062 SetToggle(characterInfoToggle,
false);
2063 ikToggle.Selected =
false;
2064 spritesheetToggle.Selected =
true;
2067 ResetParamsEditor();
2070 ragdollToggle.OnSelected = box =>
2072 editRagdoll = box.Selected;
2075 SetToggle(limbsToggle,
false);
2076 SetToggle(animsToggle,
false);
2077 SetToggle(jointsToggle,
false);
2078 SetToggle(characterInfoToggle,
false);
2079 paramsToggle.Selected =
true;
2082 ResetParamsEditor();
2085 characterInfoToggle.OnSelected = box =>
2087 editCharacterInfo = box.Selected;
2088 if (editCharacterInfo)
2090 SetToggle(limbsToggle,
false);
2091 SetToggle(animsToggle,
false);
2092 SetToggle(ragdollToggle,
false);
2093 SetToggle(jointsToggle,
false);
2094 paramsToggle.Selected =
true;
2097 ResetParamsEditor();
2100 modesToggle =
new ToggleButton(
new RectTransform(
new Vector2(0.08f, 1), modesPanel.RectTransform,
Anchor.CenterRight,
Pivot.CenterLeft),
Direction.Left);
2101 modesPanel.RectTransform.MinSize =
new Point(0, (
int)(layoutGroup.RectTransform.Children.Sum(c => c.MinSize.Y + layoutGroup.AbsoluteSpacing) * 1.2f));
2104 private void SetToggle(GUITickBox toggle,
bool value)
2106 if (toggle.Selected != value)
2110 toggle.Box.Flash(GUIStyle.Green, useRectangleFlash:
true);
2114 toggle.Box.Flash(GUIStyle.Red, useRectangleFlash:
true);
2117 toggle.Selected = value;
2120 private void CreateButtonsPanel()
2122 buttonsPanel =
new GUIFrame(
new RectTransform(
new Vector2(1.0f, 0.1f), leftArea.RectTransform));
2123 Vector2 buttonSize =
new Vector2(1, 0.45f);
2124 var parent =
new GUIFrame(
new RectTransform(
new Vector2(0.85f, 0.70f), buttonsPanel.RectTransform,
Anchor.Center), style:
null);
2125 var reloadTexturesButton =
new GUIButton(
new RectTransform(buttonSize, parent.RectTransform,
Anchor.TopCenter), GetCharacterEditorTranslation(
"ReloadTextures"));
2126 reloadTexturesButton.OnClicked += (button, userData) =>
2128 foreach (var limb
in character.AnimController.Limbs)
2130 if (limb ==
null) {
continue; }
2131 limb.ActiveSprite?.ReloadTexture();
2132 limb.WearingItems.ForEach(i => i.Sprite.ReloadTexture());
2133 limb.OtherWearables.ForEach(w => w.Sprite.ReloadTexture());
2138 var recreateButton =
new GUIButton(
new RectTransform(buttonSize, parent.RectTransform,
Anchor.BottomCenter), GetCharacterEditorTranslation(
"RecreateRagdoll"))
2140 ToolTip = GetCharacterEditorTranslation(
"RecreateRagdollTooltip"),
2141 OnClicked = (button, data) =>
2144 character.AnimController.ResetLimbs();
2148 GUITextBlock.AutoScaleAndNormalize(reloadTexturesButton.TextBlock, recreateButton.TextBlock);
2149 buttonsPanelToggle =
new ToggleButton(
new RectTransform(
new Vector2(0.08f, 1), buttonsPanel.RectTransform,
Anchor.CenterRight,
Pivot.CenterLeft),
Direction.Left);
2150 buttonsPanel.RectTransform.MinSize =
new Point(0, (
int)(parent.RectTransform.Children.Sum(c => c.MinSize.Y) * 1.5f));
2154 private void CreateOptionsPanel(Vector2 toggleSize)
2156 optionsPanel =
new GUIFrame(
new RectTransform(
new Vector2(1, 0.3f), rightArea.RectTransform));
2157 var layoutGroup =
new GUILayoutGroup(
new RectTransform(innerScale, optionsPanel.RectTransform,
Anchor.Center))
2159 AbsoluteSpacing = 2,
2162 new GUITextBlock(
new RectTransform(
new Vector2(0.03f, 0.0f), layoutGroup.RectTransform), GetCharacterEditorTranslation(
"OptionsPanel"), font: GUIStyle.LargeFont);
2163 freezeToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"Freeze"))
2168 isFrozen = box.Selected;
2172 new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"AutoFreeze"))
2177 autoFreeze = box.Selected;
2181 new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"LimbPairEditing"))
2184 Enabled = character.IsHumanoid,
2187 limbPairEditing = box.Selected;
2191 animTestPoseToggle =
new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"AnimationTestPose"))
2193 Selected = character.AnimController.AnimationTestPose,
2197 character.AnimController.AnimationTestPose = box.Selected;
2201 new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"AutoMove"))
2203 Selected = character.OverrideMovement !=
null,
2206 character.OverrideMovement = box.Selected ?
new Vector2(1, 0) as Vector2? : null;
2210 new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("FollowCursor"))
2212 Selected = !character.dontFollowCursor,
2215 character.dontFollowCursor = !box.Selected;
2219 new GUITickBox(
new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"EditBackgroundColor"))
2224 displayBackgroundColor = box.Selected;
2228 optionsToggle =
new ToggleButton(
new RectTransform(
new Vector2(0.08f, 1), optionsPanel.RectTransform,
Anchor.CenterLeft,
Pivot.CenterRight),
Direction.Right);
2229 optionsPanel.RectTransform.MinSize =
new Point(0, (
int)(layoutGroup.RectTransform.Children.Sum(c => c.MinSize.Y + layoutGroup.AbsoluteSpacing) * 1.2f));
2232 private void CreateContextualControls()
2234 Point elementSize =
new Point(120, 20).Multiply(GUI.Scale);
2235 int textAreaHeight = 20;
2237 backgroundColorPanel =
new GUIFrame(
new RectTransform(
new Vector2(0.5f, 0.1f), centerArea.RectTransform,
Anchor.TopRight)
2239 AbsoluteOffset = new Point(10, 0).Multiply(GUI.Scale)
2242 CanBeFocused =
false
2245 var frame =
new GUIFrame(
new RectTransform(
new Point(500, 80).Multiply(GUI.Scale), backgroundColorPanel.RectTransform,
Anchor.TopRight), style:
null, color: Color.Black * 0.4f);
2246 new GUITextBlock(
new RectTransform(
new Vector2(0.2f, 1), frame.RectTransform)
2248 MinSize = new Point(80, 26)
2249 }, GetCharacterEditorTranslation(
"BackgroundColor") +
":", textColor: Color.WhiteSmoke);
2250 var inputArea =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.7f, 1), frame.RectTransform,
Anchor.TopRight)
2252 AbsoluteOffset = new Point(20, 0).Multiply(GUI.Scale)
2253 }, isHorizontal:
true, childAnchor:
Anchor.CenterRight)
2256 RelativeSpacing = 0.01f
2258 var fields =
new GUIComponent[4];
2259 string[] colorComponentLabels = {
"R",
"G",
"B" };
2260 for (
int i = 2; i >= 0; i--)
2262 var element =
new GUIFrame(
new RectTransform(
new Vector2(0.3f, 1), inputArea.RectTransform)
2264 MinSize = new Point(40, 0),
2265 MaxSize = new Point(100, 50)
2266 }, style:
null, color: Color.Black * 0.6f);
2267 var colorLabel =
new GUITextBlock(
new RectTransform(
new Vector2(0.3f, 1), element.RectTransform,
Anchor.CenterLeft), colorComponentLabels[i],
2268 font: GUIStyle.SmallFont, textAlignment: Alignment.CenterLeft);
2269 GUINumberInput numberInput =
new GUINumberInput(
new RectTransform(
new Vector2(0.7f, 1), element.RectTransform,
Anchor.CenterRight),
2270 NumberType.Int, relativeButtonAreaWidth: 0.25f)
2272 Font = GUIStyle.SmallFont
2274 numberInput.MinValueInt = 0;
2275 numberInput.MaxValueInt = 255;
2276 numberInput.Font = GUIStyle.SmallFont;
2280 colorLabel.TextColor = GUIStyle.Red;
2281 numberInput.IntValue = backgroundColor.R;
2282 numberInput.OnValueChanged += (numInput) => backgroundColor.R = (
byte)numInput.IntValue;
2285 colorLabel.TextColor = GUIStyle.Green;
2286 numberInput.IntValue = backgroundColor.G;
2287 numberInput.OnValueChanged += (numInput) => backgroundColor.G = (
byte)numInput.IntValue;
2290 colorLabel.TextColor = Color.DeepSkyBlue;
2291 numberInput.IntValue = backgroundColor.B;
2292 numberInput.OnValueChanged += (numInput) => backgroundColor.B = (
byte)numInput.IntValue;
2297 spriteSheetControls =
new GUIFrame(
new RectTransform(
new Vector2(0.5f, 0.1f), centerArea.RectTransform,
Anchor.BottomLeft)
2299 RelativeOffset = new Vector2(0, 0.1f)
2302 CanBeFocused =
false
2304 var layoutGroupSpriteSheet =
new GUILayoutGroup(
new RectTransform(Vector2.One, spriteSheetControls.RectTransform))
2306 AbsoluteSpacing = 5,
2307 CanBeFocused =
false
2309 new GUITextBlock(
new RectTransform(
new Point(elementSize.X, textAreaHeight), layoutGroupSpriteSheet.RectTransform), GetCharacterEditorTranslation(
"SpriteSheetZoom") +
":", Color.White);
2310 var spriteSheetControlElement =
new GUIFrame(
new RectTransform(
new Point(elementSize.X * 2, textAreaHeight), layoutGroupSpriteSheet.RectTransform), style:
null);
2311 CalculateSpritesheetZoom();
2312 spriteSheetZoomBar =
new GUIScrollBar(
new RectTransform(
new Vector2(0.69f, 1), spriteSheetControlElement.RectTransform,
Anchor.CenterLeft), barSize: 0.2f, style:
"GUISlider")
2314 BarScroll = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(spriteSheetMinZoom, spriteSheetMaxZoom, spriteSheetZoom)),
2316 OnMoved = (scrollBar, value) =>
2318 spriteSheetZoom = MathHelper.Lerp(spriteSheetMinZoom, spriteSheetMaxZoom, value);
2322 new GUIButton(
new RectTransform(
new Vector2(0.3f, 1.25f), spriteSheetControlElement.RectTransform,
Anchor.CenterRight), GetCharacterEditorTranslation(
"Reset"), style:
"GUIButtonFreeScale")
2324 OnClicked = (box, data) =>
2326 spriteSheetZoom = Math.Min(1, spriteSheetMaxZoom);
2327 spriteSheetZoomBar.BarScroll = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(spriteSheetMinZoom, spriteSheetMaxZoom, spriteSheetZoom));
2331 new GUITickBox(
new RectTransform(
new Point(elementSize.X, textAreaHeight), layoutGroupSpriteSheet.RectTransform), GetCharacterEditorTranslation(
"HideBodySprites"))
2333 TextColor = Color.White,
2335 OnSelected = (GUITickBox box) =>
2337 hideBodySheet = box.Selected;
2341 new GUITickBox(
new RectTransform(
new Point(elementSize.X, textAreaHeight), layoutGroupSpriteSheet.RectTransform), GetCharacterEditorTranslation(
"ShowWearables"))
2343 TextColor = Color.White,
2345 OnSelected = (GUITickBox box) =>
2347 displayWearables = box.Selected;
2348 if (displayWearables)
2359 new GUITickBox(
new RectTransform(
new Point(elementSize.X, textAreaHeight), layoutGroupSpriteSheet.RectTransform), GetCharacterEditorTranslation(
"Unrestrict"))
2361 TextColor = Color.White,
2363 OnSelected = (GUITickBox box) =>
2365 SetSpritesheetRestriction(box.Selected);
2369 resetSpriteOrientationButtonParent =
new GUIFrame(
new RectTransform(
new Vector2(0.1f, 0.025f), centerArea.RectTransform,
Anchor.BottomCenter)
2371 AbsoluteOffset = new Point(0, -5).Multiply(GUI.Scale),
2372 RelativeOffset = new Vector2(-0.05f, 0)
2375 CanBeFocused =
false
2377 new GUIButton(
new RectTransform(Vector2.One, resetSpriteOrientationButtonParent.RectTransform,
Anchor.TopRight), GetCharacterEditorTranslation(
"Reset"), style:
"GUIButtonFreeScale")
2379 OnClicked = (box, data) =>
2381 IEnumerable<Limb> limbs = selectedLimbs;
2384 limbs = selectedJoints.Select(j => PlayerInput.KeyDown(Keys.LeftAlt) ? j.LimbB : j.LimbA);
2386 foreach (var limb
in limbs)
2388 TryUpdateSubParam(limb.Params,
"spriteorientation".ToIdentifier(),
float.NaN);
2389 if (limbPairEditing)
2391 UpdateOtherLimbs(limb, l => TryUpdateSubParam(l.Params,
"spriteorientation".ToIdentifier(),
float.NaN));
2398 limbControls =
new GUIFrame(
new RectTransform(Vector2.One, centerArea.RectTransform), style:
null) { CanBeFocused =
false };
2399 var layoutGroupLimbControls =
new GUILayoutGroup(
new RectTransform(Vector2.One, limbControls.RectTransform), childAnchor:
Anchor.TopLeft) { CanBeFocused =
false };
2400 lockSpriteOriginToggle =
new GUITickBox(
new RectTransform(
new Point(elementSize.X, textAreaHeight), layoutGroupLimbControls.RectTransform), GetCharacterEditorTranslation(
"LockSpriteOrigin"))
2402 TextColor = Color.White,
2404 OnSelected = (GUITickBox box) =>
2406 lockSpriteOrigin = box.Selected;
2410 new GUITickBox(
new RectTransform(
new Point(elementSize.X, textAreaHeight), layoutGroupLimbControls.RectTransform), GetCharacterEditorTranslation(
"LockSpritePosition"))
2412 TextColor = Color.White,
2414 OnSelected = (GUITickBox box) =>
2416 lockSpritePosition = box.Selected;
2420 new GUITickBox(
new RectTransform(
new Point(elementSize.X, textAreaHeight), layoutGroupLimbControls.RectTransform), GetCharacterEditorTranslation(
"LockSpriteSize"))
2422 TextColor = Color.White,
2424 OnSelected = (GUITickBox box) =>
2426 lockSpriteSize = box.Selected;
2430 recalculateColliderToggle =
new GUITickBox(
new RectTransform(
new Point(elementSize.X, textAreaHeight), layoutGroupLimbControls.RectTransform), GetCharacterEditorTranslation(
"AdjustCollider"))
2432 TextColor = Color.White,
2434 OnSelected = (GUITickBox box) =>
2436 recalculateCollider = box.Selected;
2437 showCollidersToggle.Selected = recalculateCollider;
2441 new GUITickBox(
new RectTransform(
new Point(elementSize.X, textAreaHeight), layoutGroupLimbControls.RectTransform), GetCharacterEditorTranslation(
"OnlyShowSelectedLimbs"))
2443 TextColor = Color.White,
2444 Selected = onlyShowSourceRectForSelectedLimbs,
2445 OnSelected = (GUITickBox box) =>
2447 onlyShowSourceRectForSelectedLimbs = box.Selected;
2453 Point sliderSize =
new Point(300, 20).Multiply(GUI.Scale);
2454 jointControls =
new GUIFrame(
new RectTransform(
new Vector2(0.5f, 0.075f), centerArea.RectTransform), style:
null) { CanBeFocused =
false };
2455 var layoutGroupJoints =
new GUILayoutGroup(
new RectTransform(Vector2.One, jointControls.RectTransform), childAnchor:
Anchor.TopLeft) { CanBeFocused =
false };
2456 copyJointsToggle =
new GUITickBox(
new RectTransform(
new Point(elementSize.X, textAreaHeight), layoutGroupJoints.RectTransform), GetCharacterEditorTranslation(
"CopyJointSettings"))
2458 ToolTip = GetCharacterEditorTranslation(
"CopyJointSettingsTooltip"),
2460 TextColor = copyJointSettings ? GUIStyle.Red : Color.White,
2461 OnSelected = (GUITickBox box) =>
2463 copyJointSettings = box.Selected;
2464 box.TextColor = copyJointSettings ? GUIStyle.Red : Color.White;
2469 ragdollControls =
new GUIFrame(
new RectTransform(
new Vector2(0.5f, 0.25f), centerArea.RectTransform), style:
null) { CanBeFocused =
false };
2470 var layoutGroupRagdoll =
new GUILayoutGroup(
new RectTransform(Vector2.One, ragdollControls.RectTransform), childAnchor:
Anchor.TopLeft) { CanBeFocused =
false };
2471 var uniformScalingToggle =
new GUITickBox(
new RectTransform(
new Point(elementSize.X, textAreaHeight), layoutGroupRagdoll.RectTransform), GetCharacterEditorTranslation(
"UniformScale"))
2474 OnSelected = (GUITickBox box) =>
2476 uniformScaling = box.Selected;
2480 uniformScalingToggle.TextColor = Color.White;
2481 var jointScaleElement =
new GUIFrame(
new RectTransform(sliderSize +
new Point(0, textAreaHeight), layoutGroupRagdoll.RectTransform), style:
null);
2482 var jointScaleText =
new GUITextBlock(
new RectTransform(
new Point(elementSize.X, textAreaHeight), jointScaleElement.RectTransform), $
"{GetCharacterEditorTranslation("JointScale
")}: {RagdollParams.JointScale.FormatDoubleDecimal()}", Color.WhiteSmoke, textAlignment: Alignment.Center);
2483 var limbScaleElement =
new GUIFrame(
new RectTransform(sliderSize +
new Point(0, textAreaHeight), layoutGroupRagdoll.RectTransform), style:
null);
2484 var limbScaleText =
new GUITextBlock(
new RectTransform(
new Point(elementSize.X, textAreaHeight), limbScaleElement.RectTransform), $
"{GetCharacterEditorTranslation("LimbScale
")}: {RagdollParams.LimbScale.FormatDoubleDecimal()}", Color.WhiteSmoke, textAlignment: Alignment.Center);
2485 jointScaleBar =
new GUIScrollBar(
new RectTransform(sliderSize, jointScaleElement.RectTransform,
Anchor.BottomLeft), barSize: 0.1f, style:
"GUISlider")
2487 BarScroll = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(RagdollParams.MIN_SCALE, RagdollParams.MAX_SCALE, RagdollParams.JointScale)),
2489 OnMoved = (scrollBar, value) =>
2491 float v = MathHelper.Lerp(RagdollParams.MIN_SCALE, RagdollParams.MAX_SCALE, value);
2492 UpdateJointScale(v);
2496 limbScaleBar.BarScroll = value;
2501 limbScaleBar =
new GUIScrollBar(
new RectTransform(sliderSize, limbScaleElement.RectTransform,
Anchor.BottomLeft), barSize: 0.1f, style:
"GUISlider")
2503 BarScroll = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(RagdollParams.MIN_SCALE, RagdollParams.MAX_SCALE, RagdollParams.LimbScale)),
2505 OnMoved = (scrollBar, value) =>
2507 float v = MathHelper.Lerp(RagdollParams.MIN_SCALE, RagdollParams.MAX_SCALE, value);
2511 UpdateJointScale(v);
2512 jointScaleBar.BarScroll = value;
2517 void UpdateJointScale(
float value)
2519 freezeToggle.Selected =
false;
2520 TryUpdateRagdollParam(
"jointscale", value);
2521 jointScaleText.Text = $
"{GetCharacterEditorTranslation("JointScale
")}: {RagdollParams.JointScale.FormatDoubleDecimal()}";
2522 character.AnimController.ResetJoints();
2524 void UpdateLimbScale(
float value)
2526 TryUpdateRagdollParam(
"limbscale", value);
2527 limbScaleText.Text = $
"{GetCharacterEditorTranslation("LimbScale
")}: {RagdollParams.LimbScale.FormatDoubleDecimal()}";
2530 limbScaleBar.Bar.OnClicked += (button, data) =>
2533 RagdollParams.StoreSnapshot();
2536 jointScaleBar.Bar.OnClicked += (button, data) =>
2542 RagdollParams.StoreSnapshot();
2547 Point buttonSize =
new Point(200, 40).Multiply(GUI.Scale);
2548 extraRagdollControls =
new GUIFrame(
new RectTransform(
new Point(buttonSize.X, buttonSize.Y * 4), centerArea.RectTransform,
Anchor.BottomRight)
2550 AbsoluteOffset = new Point(30, 0).Multiply(GUI.Scale),
2551 MinSize = new Point(0, 120)
2552 }, style:
null, color: Color.Black)
2554 CanBeFocused =
false
2556 var paddedFrame =
new GUILayoutGroup(
new RectTransform(Vector2.One * 0.95f, extraRagdollControls.RectTransform,
Anchor.Center))
2561 var buttons = GUI.CreateButtons(4,
new Vector2(1, 0.25f), paddedFrame.RectTransform,
Anchor.TopCenter, style:
"GUIButtonSmallFreeScale");
2562 deleteSelectedButton = buttons[0];
2563 deleteSelectedButton.Text = GetCharacterEditorTranslation(
"DeleteSelected");
2564 deleteSelectedButton.OnClicked = (button, data) =>
2569 duplicateLimbButton = buttons[1];
2570 duplicateLimbButton.Text = GetCharacterEditorTranslation(
"DuplicateLimb");
2571 duplicateLimbButton.OnClicked = (button, data) =>
2573 CopyLimb(selectedLimbs.FirstOrDefault());
2576 createJointButton = buttons[2];
2577 createJointButton.Text = GetCharacterEditorTranslation(
"CreateJoint");
2578 createJointButton.OnClicked = (button, data) =>
2580 ToggleJointCreationMode();
2583 createLimbButton = buttons[3];
2584 createLimbButton.Text = GetCharacterEditorTranslation(
"CreateLimb");
2585 createLimbButton.OnClicked = (button, data) =>
2587 ToggleLimbCreationMode();
2590 GUITextBlock.AutoScaleAndNormalize(buttons.Select(b => b.TextBlock));
2593 animationControls =
new GUIFrame(
new RectTransform(Vector2.One, centerArea.RectTransform), style:
null) { CanBeFocused =
false };
2594 var layoutGroupAnimation =
new GUILayoutGroup(
new RectTransform(Vector2.One, animationControls.RectTransform), childAnchor:
Anchor.TopLeft) { CanBeFocused =
false };
2595 var animationSelectionElement =
new GUIFrame(
new RectTransform(
new Point(elementSize.X * 2 - (
int)(5 * GUI.xScale), elementSize.Y), layoutGroupAnimation.RectTransform), style:
null);
2596 var animationSelectionText =
new GUITextBlock(
new RectTransform(
new Point(elementSize.X, elementSize.Y), animationSelectionElement.RectTransform), GetCharacterEditorTranslation(
"SelectedAnimation"), Color.WhiteSmoke, textAlignment: Alignment.CenterRight);
2597 animSelection =
new GUIDropDown(
new RectTransform(
new Point((
int)(150 * GUI.xScale), elementSize.Y), animationSelectionElement.RectTransform,
Anchor.Center,
Pivot.CenterLeft), elementCount: 5);
2598 if (character.AnimController.CanWalk)
2605 if (character.AnimController.CanWalk && character.IsHumanoid)
2609 if (character.AnimController.ForceSelectAnimationType ==
AnimationType.NotDefined)
2615 animSelection.SelectItem(character.AnimController.ForceSelectAnimationType);
2617 animSelection.OnSelected += (element, data) =>
2619 AnimationType previousAnim = character.AnimController.ForceSelectAnimationType;
2620 character.AnimController.ForceSelectAnimationType = (
AnimationType)data;
2621 switch (character.AnimController.ForceSelectAnimationType)
2626 character.AnimController.forceStanding =
true;
2627 character.ForceRun = character.AnimController.ForceSelectAnimationType ==
AnimationType.Run;
2628 if (!wallCollisionsEnabled)
2630 SetWallCollisions(
true);
2634 TeleportTo(spawnPosition);
2638 character.AnimController.forceStanding =
false;
2639 character.ForceRun =
false;
2640 if (wallCollisionsEnabled)
2642 SetWallCollisions(
false);
2646 character.AnimController.forceStanding =
false;
2647 character.ForceRun =
true;
2648 if (wallCollisionsEnabled)
2650 SetWallCollisions(
false);
2654 throw new NotImplementedException();
2656 ResetParamsEditor();
2661 private void CreateCharacterSelectionPanel()
2663 characterSelectionPanel =
new GUIFrame(
new RectTransform(
new Vector2(1, 0.2f), rightArea.RectTransform));
2664 var content =
new GUILayoutGroup(
new RectTransform(innerScale, characterSelectionPanel.RectTransform,
Anchor.Center))
2669 var characterLabel =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.0f), content.RectTransform), GetCharacterEditorTranslation(
"CharacterPanel"), font: GUIStyle.LargeFont);
2670 var characterDropDown =
new GUIDropDown(
new RectTransform(
new Vector2(1, 0.2f), content.RectTransform)
2672 RelativeOffset = new Vector2(0, 0.2f)
2673 }, elementCount: 8, style:
null);
2674 characterDropDown.ListBox.Color =
new Color(characterDropDown.ListBox.Color.R, characterDropDown.ListBox.Color.G, characterDropDown.ListBox.Color.B,
byte.MaxValue);
2675 foreach (CharacterPrefab prefab
in CharacterPrefab.Prefabs.OrderByDescending(p => p.Identifier))
2677 Identifier speciesName = prefab.Identifier;
2678 if (ShowCreature(prefab))
2680 characterDropDown.AddItem(speciesName.Value.CapitaliseFirstInvariant(), speciesName).SetAsFirstChild();
2682 else if (!CreatureMetrics.Encountered.Contains(speciesName))
2685 var element = characterDropDown.AddItem(TextManager.Get(
"hiddensubmarines"), Identifier.Empty, textColor: Color.Gray * 0.75f);
2686 element.SetAsLastChild();
2687 element.Enabled =
false;
2690 characterDropDown.SelectItem(currentCharacterIdentifier);
2691 characterDropDown.OnSelected = (component, data) =>
2693 Identifier characterIdentifier = (Identifier)data;
2694 if (characterIdentifier.IsEmpty) {
return true; }
2697 SpawnCharacter(characterIdentifier);
2701 HandleSpawnException(characterIdentifier, e);
2705 if (currentCharacterIdentifier == CharacterPrefab.HumanSpeciesName)
2707 var jobDropDown =
new GUIDropDown(
new RectTransform(
new Vector2(1, 0.15f), content.RectTransform)
2709 RelativeOffset = new Vector2(0, 0.45f)
2710 }, elementCount: 8, style:
null);
2711 jobDropDown.ListBox.Color =
new Color(jobDropDown.ListBox.Color.R, jobDropDown.ListBox.Color.G, jobDropDown.ListBox.Color.B,
byte.MaxValue);
2712 jobDropDown.AddItem(
"None");
2713 JobPrefab.Prefabs.ForEach(j => jobDropDown.AddItem(j.Name, j.Identifier));
2714 jobDropDown.SelectItem(selectedJob);
2715 jobDropDown.OnSelected = (component, data) =>
2717 Identifier newJob = data is Identifier jobIdentifier ? jobIdentifier : Identifier.Empty;
2718 if (newJob != selectedJob)
2720 selectedJob = newJob;
2721 SpawnCharacter(currentCharacterIdentifier);
2726 var charButtons =
new GUIFrame(
new RectTransform(
new Vector2(1, 0.25f), parent: content.RectTransform, anchor:
Anchor.BottomLeft), style:
null);
2727 var prevCharacterButton =
new GUIButton(
new RectTransform(
new Vector2(0.5f, 1.0f), charButtons.RectTransform,
Anchor.TopLeft), GetCharacterEditorTranslation(
"PreviousCharacter"));
2728 prevCharacterButton.TextBlock.AutoScaleHorizontal =
true;
2729 prevCharacterButton.OnClicked += (b, obj) =>
2731 Identifier characterIdentifier = GetPreviousCharacterIdentifier();
2734 SpawnCharacter(characterIdentifier);
2738 HandleSpawnException(characterIdentifier, e);
2742 var nextCharacterButton =
new GUIButton(
new RectTransform(
new Vector2(0.5f, 1.0f), charButtons.RectTransform,
Anchor.TopRight), GetCharacterEditorTranslation(
"NextCharacter"));
2743 prevCharacterButton.TextBlock.AutoScaleHorizontal =
true;
2744 nextCharacterButton.OnClicked += (b, obj) =>
2746 Identifier characterIdentifier = GetNextCharacterIdentifier();
2749 SpawnCharacter(characterIdentifier);
2753 HandleSpawnException(characterIdentifier, e);
2757 charButtons.RectTransform.MinSize =
new Point(0, prevCharacterButton.RectTransform.MinSize.Y);
2758 characterPanelToggle =
new ToggleButton(
new RectTransform(
new Vector2(0.08f, 1), characterSelectionPanel.RectTransform,
Anchor.CenterLeft,
Pivot.CenterRight),
Direction.Right);
2759 characterSelectionPanel.RectTransform.MinSize =
new Point(0, (
int)(content.RectTransform.Children.Sum(c => c.MinSize.Y) * 1.2f));
2761 void HandleSpawnException(Identifier characterIdentifier, Exception e)
2763 if (characterIdentifier != CharacterPrefab.HumanSpeciesName)
2765 DebugConsole.ThrowError($
"Failed to spawn the character \"{characterIdentifier}\".", e);
2766 SpawnCharacter(CharacterPrefab.HumanSpeciesName);
2770 throw new Exception($
"Failed to spawn the character \"{characterIdentifier}\".", innerException: e);
2775 private void CreateFileEditPanel()
2777 Vector2 buttonSize =
new Vector2(1, 0.04f);
2779 fileEditPanel =
new GUIFrame(
new RectTransform(
new Vector2(1, 0.4f), rightArea.RectTransform));
2780 var layoutGroup =
new GUILayoutGroup(
new RectTransform(innerScale, fileEditPanel.RectTransform,
Anchor.Center))
2782 AbsoluteSpacing = 1,
2786 new GUITextBlock(
new RectTransform(
new Vector2(0.03f, 0.0f), layoutGroup.RectTransform), GetCharacterEditorTranslation(
"FileEditPanel"), font: GUIStyle.LargeFont);
2789 new GUIFrame(
new RectTransform(buttonSize / 2, layoutGroup.RectTransform), style:
null) { CanBeFocused =
false };
2790 var saveAllButton =
new GUIButton(
new RectTransform(buttonSize, layoutGroup.RectTransform), TextManager.Get(
"editor.saveall"));
2791 saveAllButton.Color = GUIStyle.Green;
2792 saveAllButton.OnClicked += (button, userData) =>
2795 if (VanillaCharacters.Contains(CharacterPrefab.Prefabs[currentCharacterIdentifier].ContentFile))
2797 GUI.AddMessage(GetCharacterEditorTranslation(
"CannotEditVanillaCharacters"), GUIStyle.Red, font: GUIStyle.LargeFont);
2801 ContentPath texturePath = ContentPath.FromRaw(character.Prefab.ContentPackage, RagdollParams.Texture);
2802 if (!character.IsHuman && (texturePath.IsNullOrWhiteSpace() || !File.Exists(texturePath.Value)))
2804 DebugConsole.ThrowError($
"Invalid texture path: {RagdollParams.Texture}");
2809 character.Params.Save();
2810 GUI.AddMessage(GetCharacterEditorTranslation(
"CharacterSavedTo").Replace(
"[path]", CharacterParams.Path.Value), GUIStyle.Green, font: GUIStyle.Font, lifeTime: 5);
2811 character.AnimController.SaveRagdoll();
2812 GUI.AddMessage(GetCharacterEditorTranslation(
"RagdollSavedTo").Replace(
"[path]", RagdollParams.Path.Value), GUIStyle.Green, font: GUIStyle.Font, lifeTime: 5);
2813 AnimParams.ForEach(p => p.Save());
2819 new GUIFrame(
new RectTransform(buttonSize / 2, layoutGroup.RectTransform), style:
null) { CanBeFocused =
false };
2821 Vector2 messageBoxRelSize =
new Vector2(0.5f, 0.7f);
2822 var saveRagdollButton =
new GUIButton(
new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"SaveRagdoll"));
2823 saveRagdollButton.OnClicked += (button, userData) =>
2825 var box =
new GUIMessageBox(GetCharacterEditorTranslation(
"SaveRagdoll"), $
"{GetCharacterEditorTranslation("ProvideFileName
")}: ",
new LocalizedString[] { TextManager.Get(
"Cancel"), TextManager.Get(
"Save") }, messageBoxRelSize);
2826 var inputField =
new GUITextBox(
new RectTransform(
new Point(box.Content.Rect.Width, (
int)(30 * GUI.yScale)), box.Content.RectTransform,
Anchor.Center), RagdollParams.Name.RemoveWhitespace());
2827 box.Buttons[0].OnClicked += (b, d) =>
2832 box.Buttons[1].OnClicked += (b, d) =>
2835 if (VanillaCharacters.Contains(CharacterPrefab.Prefabs[currentCharacterIdentifier].ContentFile))
2837 GUI.AddMessage(GetCharacterEditorTranslation(
"CannotEditVanillaCharacters"), GUIStyle.Red, font: GUIStyle.LargeFont);
2842 character.AnimController.SaveRagdoll(inputField.Text);
2843 GUI.AddMessage(GetCharacterEditorTranslation(
"RagdollSavedTo").Replace(
"[path]", RagdollParams.Path.Value), Color.Green, font: GUIStyle.Font);
2844 RagdollParams.ClearCache();
2845 ResetParamsEditor();
2851 var loadRagdollButton =
new GUIButton(
new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"LoadRagdoll"));
2852 loadRagdollButton.OnClicked += (button, userData) =>
2854 var loadBox =
new GUIMessageBox(GetCharacterEditorTranslation(
"LoadRagdoll"),
"",
new LocalizedString[] { TextManager.Get(
"Cancel"), TextManager.Get(
"Load"), TextManager.Get(
"Delete") }, messageBoxRelSize);
2855 loadBox.Buttons[0].OnClicked += loadBox.Close;
2856 var listBox =
new GUIListBox(
new RectTransform(
new Vector2(0.9f, 0.6f), loadBox.Content.RectTransform,
Anchor.TopCenter))
2858 PlaySoundOnSelect =
true,
2860 var deleteButton = loadBox.Buttons[2];
2861 deleteButton.Enabled =
false;
2862 void PopulateListBox()
2866 var filePaths = Directory.GetFiles(RagdollParams.Folder);
2867 foreach (var path
in filePaths)
2869 GUITextBlock textBlock =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.1f), listBox.Content.RectTransform) { MinSize = new Point(0, 30) },
2870 ToolBox.LimitString(Path.GetFileNameWithoutExtension(path), GUIStyle.Font, listBox.Rect.Width - 80))
2879 DebugConsole.ThrowErrorLocalized(GetCharacterEditorTranslation(
"CouldntOpenDirectory").Replace(
"[folder]", RagdollParams.Folder), e);
2884 string selectedFile =
null;
2885 listBox.OnSelected += (component, data) =>
2887 selectedFile = data as string;
2889 var fileName = Path.GetFileNameWithoutExtension(selectedFile);
2890 deleteButton.Enabled = fileName != RagdollParams.Name && fileName != RagdollParams.GetDefaultFileName(character.SpeciesName);
2893 deleteButton.OnClicked += (btn, data) =>
2895 if (selectedFile ==
null)
2900 var msgBox =
new GUIMessageBox(
2901 TextManager.Get(
"DeleteDialogLabel"),
2902 TextManager.GetWithVariable(
"DeleteDialogQuestion",
"[file]", selectedFile),
2903 new LocalizedString[] { TextManager.Get(
"Yes"), TextManager.Get(
"Cancel") });
2904 msgBox.Buttons[0].OnClicked += (b, d) =>
2908 File.Delete(selectedFile);
2909 GUI.AddMessage(GetCharacterEditorTranslation(
"RagdollDeletedFrom").Replace(
"[file]", selectedFile), GUIStyle.Red, font: GUIStyle.Font);
2913 DebugConsole.ThrowErrorLocalized(TextManager.Get(
"DeleteFileError").Replace(
"[file]", selectedFile), e);
2916 listBox.ClearChildren();
2918 selectedFile =
null;
2921 msgBox.Buttons[1].OnClicked += (b, d) =>
2928 loadBox.Buttons[1].OnClicked += (btn, data) =>
2930 string fileName = Path.GetFileNameWithoutExtension(selectedFile);
2931 Identifier baseSpecies = character.GetBaseCharacterSpeciesName();
2932 var ragdoll = character.IsHumanoid
2933 ? RagdollParams.GetRagdollParams<HumanRagdollParams>(character.SpeciesName, baseSpecies, fileName, character.Prefab.ContentPackage) as RagdollParams
2934 : RagdollParams.GetRagdollParams<FishRagdollParams>(character.SpeciesName, baseSpecies, fileName, character.Prefab.ContentPackage);
2935 ragdoll.Reset(
true);
2936 GUI.AddMessage(GetCharacterEditorTranslation(
"RagdollLoadedFrom").Replace(
"[file]", selectedFile), Color.WhiteSmoke, font: GUIStyle.Font);
2937 RecreateRagdoll(ragdoll);
2938 CreateContextualControls();
2944 var saveAnimationButton =
new GUIButton(
new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"SaveAnimation"));
2945 saveAnimationButton.OnClicked += (button, userData) =>
2947 var box =
new GUIMessageBox(GetCharacterEditorTranslation(
"SaveAnimation"),
string.Empty,
new LocalizedString[] { TextManager.Get(
"Cancel"), TextManager.Get(
"Save") }, messageBoxRelSize);
2948 var textArea =
new GUIFrame(
new RectTransform(
new Vector2(1, 0.1f), box.Content.RectTransform) { MinSize = new Point(350, 30) }, style:
null);
2949 var inputLabel =
new GUITextBlock(
new RectTransform(
new Vector2(0.3f, 1), textArea.RectTransform,
Anchor.CenterLeft) { MinSize = new Point(250, 30) }, $
"{GetCharacterEditorTranslation("ProvideFileName
")}: ");
2950 var inputField =
new GUITextBox(
new RectTransform(
new Vector2(0.45f, 1), textArea.RectTransform,
Anchor.CenterRight) { MinSize = new Point(100, 30) }, CurrentAnimation.Name);
2952 var typeSelectionArea =
new GUIFrame(
new RectTransform(
new Vector2(1f, 0.1f), box.Content.RectTransform) { MinSize = new Point(0, 30) }, style:
null);
2953 var typeLabel =
new GUITextBlock(
new RectTransform(
new Vector2(0.45f, 1), typeSelectionArea.RectTransform,
Anchor.CenterLeft), $
"{GetCharacterEditorTranslation("SelectAnimationType
")}: ");
2954 var typeDropdown =
new GUIDropDown(
new RectTransform(
new Vector2(0.45f, 1), typeSelectionArea.RectTransform,
Anchor.CenterRight), elementCount: 4);
2955 foreach (
object enumValue
in Enum.GetValues(typeof(
AnimationType)))
2959 typeDropdown.AddItem(enumValue.ToString(), enumValue);
2962 AnimationType selectedType = character.AnimController.ForceSelectAnimationType;
2963 typeDropdown.OnSelected = (component, data) =>
2966 inputField.Text = character.AnimController.GetAnimationParamsFromType(selectedType)?.Name.RemoveWhitespace();
2969 typeDropdown.SelectItem(selectedType);
2970 box.Buttons[0].OnClicked += (b, d) =>
2975 box.Buttons[1].OnClicked += (b, d) =>
2978 if (VanillaCharacters.Contains(CharacterPrefab.Prefabs[currentCharacterIdentifier].ContentFile))
2980 GUI.AddMessage(GetCharacterEditorTranslation(
"CannotEditVanillaCharacters"), GUIStyle.Red, font: GUIStyle.LargeFont);
2985 var animParams = character.AnimController.GetAnimationParamsFromType(selectedType);
2986 if (animParams ==
null) {
return true; }
2987 string fileName = inputField.Text;
2988 animParams.Save(fileName);
2989 string newPath = animParams.Path.ToString();
2990 GUI.AddMessage(GetCharacterEditorTranslation(
"AnimationOfTypeSavedTo").Replace(
"[type]", selectedType.ToString()).Replace(
"[path]", newPath), Color.Green, font: GUIStyle.Font);
2991 AnimationParams.ClearCache();
2992 ResetParamsEditor();
2998 var loadAnimationButton =
new GUIButton(
new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"LoadAnimation"));
2999 loadAnimationButton.OnClicked += (button, userData) =>
3001 var loadBox =
new GUIMessageBox(GetCharacterEditorTranslation(
"LoadAnimation"),
"",
new LocalizedString[] { TextManager.Get(
"Cancel"), TextManager.Get(
"Load"), TextManager.Get(
"Delete") }, messageBoxRelSize);
3002 loadBox.Buttons[0].OnClicked += loadBox.Close;
3003 var listBox =
new GUIListBox(
new RectTransform(
new Vector2(0.9f, 0.6f), loadBox.Content.RectTransform))
3005 PlaySoundOnSelect =
true,
3007 var deleteButton = loadBox.Buttons[2];
3008 deleteButton.Enabled =
false;
3010 var typeSelectionArea =
new GUIFrame(
new RectTransform(
new Vector2(0.9f, 0.1f), loadBox.Content.RectTransform) { MinSize = new Point(0, 30) }, style:
null);
3011 var typeLabel =
new GUITextBlock(
new RectTransform(
new Vector2(0.45f, 1), typeSelectionArea.RectTransform,
Anchor.CenterLeft), $
"{GetCharacterEditorTranslation("SelectAnimationType
")}: ");
3012 var typeDropdown =
new GUIDropDown(
new RectTransform(
new Vector2(0.45f, 1), typeSelectionArea.RectTransform,
Anchor.CenterRight), elementCount: 4);
3013 foreach (
object enumValue
in Enum.GetValues(typeof(
AnimationType)))
3017 typeDropdown.AddItem(enumValue.ToString(), enumValue);
3020 AnimationType selectedType = character.AnimController.ForceSelectAnimationType;
3021 typeDropdown.OnSelected = (component, data) =>
3027 typeDropdown.SelectItem(selectedType);
3028 void PopulateListBox()
3032 listBox.ClearChildren();
3033 var filePaths = Directory.GetFiles(CurrentAnimation.Folder);
3034 foreach (var path
in AnimationParams.FilterAndSortFiles(filePaths, selectedType))
3036 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))
3045 DebugConsole.ThrowErrorLocalized(GetCharacterEditorTranslation(
"CouldntOpenDirectory").Replace(
"[folder]", CurrentAnimation.Folder), e);
3050 string selectedFile =
null;
3051 listBox.OnSelected += (component, data) =>
3053 selectedFile = data as string;
3055 string fileName = Path.GetFileNameWithoutExtension(selectedFile);
3056 deleteButton.Enabled = fileName != CurrentAnimation.Name && fileName != AnimationParams.GetDefaultFileName(character.SpeciesName, CurrentAnimation.AnimationType);
3059 deleteButton.OnClicked += (btn, data) =>
3061 if (selectedFile ==
null)
3066 var msgBox =
new GUIMessageBox(
3067 TextManager.Get(
"DeleteDialogLabel"),
3068 TextManager.GetWithVariable(
"DeleteDialogQuestion",
"[file]", selectedFile),
3069 new LocalizedString[] { TextManager.Get(
"Yes"), TextManager.Get(
"Cancel") });
3070 msgBox.Buttons[0].OnClicked += (b, d) =>
3074 File.Delete(selectedFile);
3075 GUI.AddMessage(GetCharacterEditorTranslation(
"AnimationOfTypeDeleted").Replace(
"[type]", selectedType.ToString()).Replace(
"[file]", selectedFile), GUIStyle.Red, font: GUIStyle.Font);
3079 DebugConsole.ThrowErrorLocalized(TextManager.GetWithVariable(
"DeleteFileError",
"[file]", selectedFile), e);
3083 selectedFile =
null;
3086 msgBox.Buttons[1].OnClicked += (b, d) =>
3093 loadBox.Buttons[1].OnClicked += (btn, data) =>
3095 if (character.AnimController.TryLoadAnimation(selectedType, Path.GetFileNameWithoutExtension(selectedFile), out AnimationParams animationParams, throwErrors:
true))
3097 animationParams.Reset(forceReload:
true);
3098 GUI.AddMessage(GetCharacterEditorTranslation(
"AnimationOfTypeLoaded").Replace(
"[type]", selectedType.ToString()).Replace(
"[file]", animationParams.FileNameWithoutExtension), Color.WhiteSmoke, font: GUIStyle.Font);
3100 ResetParamsEditor();
3108 new GUIFrame(
new RectTransform(buttonSize / 2, layoutGroup.RectTransform), style:
null) { CanBeFocused =
false };
3109 var resetButton =
new GUIButton(
new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"ResetButton"));
3110 resetButton.Color = GUIStyle.Red;
3111 resetButton.OnClicked += (button, userData) =>
3113 CharacterParams.Reset(
true);
3114 AnimParams.ForEach(p => p.Reset(
true));
3115 character.AnimController.ResetRagdoll(forceReload:
true);
3117 jointCreationMode = JointCreationMode.None;
3118 isDrawingLimb =
false;
3120 jointStartLimb =
null;
3126 new GUIFrame(
new RectTransform(buttonSize / 2, layoutGroup.RectTransform), style:
null) { CanBeFocused =
false };
3127 new GUIButton(
new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"CreateNewCharacter"))
3129 OnClicked = (button, data) =>
3132 Wizard.Instance.SelectTab(Wizard.Tab.Character);
3136 new GUIButton(
new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation(
"CopyCharacter"))
3138 ToolTip = GetCharacterEditorTranslation(
"CopyCharacterToolTip"),
3139 OnClicked = (button, data) =>
3142 PrepareCharacterCopy();
3143 Wizard.Instance.SelectTab(Wizard.Tab.Character);
3148 GUITextBlock.AutoScaleAndNormalize(layoutGroup.Children.Where(c => c is GUIButton).Select(c => ((GUIButton)c).TextBlock));
3150 fileEditToggle =
new ToggleButton(
new RectTransform(
new Vector2(0.08f, 1), fileEditPanel.RectTransform,
Anchor.CenterLeft,
Pivot.CenterRight),
Direction.Right);
3154 characterInfoToggle.Selected =
false;
3155 ragdollToggle.Selected =
false;
3156 limbsToggle.Selected =
false;
3157 animsToggle.Selected =
false;
3158 spritesheetToggle.Selected =
false;
3159 jointsToggle.Selected =
false;
3160 paramsToggle.Selected =
false;
3161 skeletonToggle.Selected =
false;
3162 damageModifiersToggle.Selected =
false;
3165 fileEditPanel.RectTransform.MinSize =
new Point(0, (
int)(layoutGroup.RectTransform.Children.Sum(c => c.MinSize.Y + layoutGroup.AbsoluteSpacing) * 1.2f));
3173 AnimParams.ForEach(a => a.Serialize());
3177 #region ToggleButtons
3184 private class ToggleButton
3189 public float OpenState {
get;
private set; } = 1;
3191 private bool isHidden;
3192 public bool IsHidden
3194 get {
return isHidden; }
3198 RefreshToggleButtonState();
3202 public ToggleButton(RectTransform rectT,
Direction dir)
3204 toggleButton =
new GUIButton(rectT, style:
"UIToggleButton")
3206 OnClicked = (button, data) =>
3208 IsHidden = !IsHidden;
3213 RefreshToggleButtonState();
3216 public void RefreshToggleButtonState()
3218 foreach (GUIComponent child
in toggleButton.Children)
3223 child.SpriteEffects = isHidden ? SpriteEffects.None : SpriteEffects.FlipHorizontally;
3226 child.SpriteEffects = isHidden ? SpriteEffects.FlipHorizontally : SpriteEffects.None;
3232 public void UpdateOpenState(
float deltaTime, Vector2 hiddenPos, RectTransform panel)
3234 panel.AbsoluteOffset =
new Vector2(MathHelper.SmoothStep(hiddenPos.X, 0.0f, OpenState), panel.AbsoluteOffset.Y).ToPoint();
3235 OpenState = isHidden ? Math.Max(OpenState - deltaTime * 5, 0) : Math.Min(OpenState + deltaTime * 5, 1);
3242 private CharacterParams CharacterParams => character.Params;
3243 private List<AnimationParams> AnimParams => character.AnimController.AllAnimParams;
3244 private AnimationParams CurrentAnimation => character.AnimController.CurrentAnimationParams;
3245 private RagdollParams RagdollParams => character.AnimController.RagdollParams;
3247 private void ResetParamsEditor()
3249 ParamsEditor.Instance.Clear();
3250 if (!editRagdoll && !editCharacterInfo && !editJoints && !editLimbs && !editAnimations)
3252 paramsToggle.Selected =
false;
3255 if (editCharacterInfo)
3257 var mainEditor = ParamsEditor.Instance;
3258 CharacterParams.AddToEditor(mainEditor, space: 10);
3259 var characterEditor = CharacterParams.SerializableEntityEditor;
3261 characterEditor.AddCustomContent(
new GUIFrame(
new RectTransform(
new Point(characterEditor.Rect.Width, (
int)(10 * GUI.yScale)), characterEditor.RectTransform), style:
null) { CanBeFocused = false }, 1);
3262 if (CharacterParams.AI !=
null)
3264 CreateAddButton(CharacterParams.AI.SerializableEntityEditor, () => CharacterParams.AI.TryAddEmptyTarget(out _), GetCharacterEditorTranslation(
"AddAITarget"));
3265 foreach (var target
in CharacterParams.AI.Targets)
3267 CreateCloseButton(target.SerializableEntityEditor, () => CharacterParams.AI.RemoveTarget(target), size: 0.8f);
3270 foreach (var emitter
in CharacterParams.BloodEmitters)
3272 CreateCloseButton(emitter.SerializableEntityEditor, () => CharacterParams.RemoveBloodEmitter(emitter));
3274 foreach (var emitter
in CharacterParams.GibEmitters)
3276 CreateCloseButton(emitter.SerializableEntityEditor, () => CharacterParams.RemoveGibEmitter(emitter));
3278 foreach (var emitter
in CharacterParams.DamageEmitters)
3280 CreateCloseButton(emitter.SerializableEntityEditor, () => CharacterParams.RemoveDamageEmitter(emitter));
3282 foreach (var sound
in CharacterParams.Sounds)
3284 CreateCloseButton(sound.SerializableEntityEditor, () => CharacterParams.RemoveSound(sound));
3286 foreach (var inventory
in CharacterParams.Inventories)
3288 var editor = inventory.SerializableEntityEditor;
3289 CreateCloseButton(editor, () => CharacterParams.RemoveInventory(inventory));
3290 foreach (var item
in inventory.Items)
3292 CreateCloseButton(item.SerializableEntityEditor, () => inventory.RemoveItem(item), size: 0.8f);
3294 CreateAddButton(editor, () => inventory.AddItem(), GetCharacterEditorTranslation(
"AddInventoryItem"));
3296 CreateAddButtonAtLast(mainEditor, () => CharacterParams.AddBloodEmitter(), GetCharacterEditorTranslation(
"AddBloodEmitter"));
3297 CreateAddButtonAtLast(mainEditor, () => CharacterParams.AddGibEmitter(), GetCharacterEditorTranslation(
"AddGibEmitter"));
3298 CreateAddButtonAtLast(mainEditor, () => CharacterParams.AddDamageEmitter(), GetCharacterEditorTranslation(
"AddDamageEmitter"));
3299 CreateAddButtonAtLast(mainEditor, () => CharacterParams.AddSound(), GetCharacterEditorTranslation(
"AddSound"));
3300 CreateAddButtonAtLast(mainEditor, () => CharacterParams.AddInventory(), GetCharacterEditorTranslation(
"AddInventory"));
3302 else if (editAnimations)
3304 character.AnimController.CurrentAnimationParams?.AddToEditor(ParamsEditor.Instance, space: 10);
3310 RagdollParams.AddToEditor(ParamsEditor.Instance, alsoChildren:
false, space: 10);
3311 RagdollParams.Colliders.ForEach(c => c.AddToEditor(ParamsEditor.Instance,
false, 10));
3313 else if (editJoints)
3315 if (selectedJoints.Any())
3317 selectedJoints.ForEach(j => j.Params.AddToEditor(ParamsEditor.Instance,
true, space: 10));
3321 RagdollParams.Joints.ForEach(jp => jp.AddToEditor(ParamsEditor.Instance,
false, space: 10));
3326 if (selectedLimbs.Any())
3328 foreach (var limb
in selectedLimbs)
3330 var mainEditor = ParamsEditor.Instance;
3331 var limbEditor = limb.Params.SerializableEntityEditor;
3332 limb.Params.AddToEditor(mainEditor,
true, space: 0);
3333 foreach (var damageModifier
in limb.Params.DamageModifiers)
3335 CreateCloseButton(damageModifier.SerializableEntityEditor, () => limb.Params.RemoveDamageModifier(damageModifier));
3337 if (limb.Params.Sound ==
null)
3339 CreateAddButtonAtLast(mainEditor, () => limb.Params.AddSound(), GetCharacterEditorTranslation(
"AddSound"));
3343 CreateCloseButton(limb.Params.Sound.SerializableEntityEditor, () => limb.Params.RemoveSound());
3345 if (limb.Params.LightSource ==
null)
3347 CreateAddButtonAtLast(mainEditor, () => limb.Params.AddLight(), GetCharacterEditorTranslation(
"AddLightSource"));
3351 CreateCloseButton(limb.Params.LightSource.SerializableEntityEditor, () => limb.Params.RemoveLight());
3353 if (limb.Params.Attack ==
null)
3355 CreateAddButtonAtLast(mainEditor, () => limb.Params.AddAttack(), GetCharacterEditorTranslation(
"AddAttack"));
3359 var attackParams = limb.Params.Attack;
3360 foreach (var affliction
in attackParams.Attack.Afflictions)
3362 if (attackParams.AfflictionEditors.TryGetValue(affliction.Key, out SerializableEntityEditor afflictionEditor))
3364 CreateCloseButton(afflictionEditor, () => attackParams.RemoveAffliction(affliction.Value), size: 0.8f);
3367 var attackEditor = attackParams.SerializableEntityEditor;
3368 CreateAddButton(attackEditor, () => attackParams.AddNewAffliction(), GetCharacterEditorTranslation(
"AddAffliction"));
3369 CreateCloseButton(attackEditor, () => limb.Params.RemoveAttack());
3370 var space =
new GUIFrame(
new RectTransform(
new Point(attackEditor.RectTransform.Rect.Width, (
int)(20 * GUI.yScale)), attackEditor.RectTransform), style:
null, color: ParamsEditor.Color)
3372 CanBeFocused =
false
3374 attackEditor.AddCustomContent(space, attackEditor.ContentCount);
3376 CreateAddButtonAtLast(mainEditor, () => limb.Params.AddDamageModifier(), GetCharacterEditorTranslation(
"AddDamageModifier"));
3381 character.AnimController.Limbs.ForEach(l => l.Params.AddToEditor(ParamsEditor.Instance,
false, space: 10));
3386 void CreateCloseButton(SerializableEntityEditor editor, Action onButtonClicked,
float size = 1)
3388 if (editor ==
null) {
return; }
3390 var parent =
new GUIFrame(
new RectTransform(
new Point(editor.Rect.Width, (
int)(height * size * GUI.yScale)), editor.RectTransform, isFixedSize:
true), style:
null)
3392 CanBeFocused =
false
3394 new GUIButton(
new RectTransform(
new Vector2(0.9f), parent.RectTransform,
Anchor.BottomRight, scaleBasis:
ScaleBasis.BothHeight), style:
"GUICancelButton", color: GUIStyle.Red)
3396 OnClicked = (button, data) =>
3399 ResetParamsEditor();
3403 editor.AddCustomContent(parent, 0);
3406 void CreateAddButtonAtLast(ParamsEditor editor, Action onButtonClicked, LocalizedString text)
3408 if (editor ==
null) {
return; }
3409 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)
3411 CanBeFocused =
false
3413 new GUIButton(
new RectTransform(
new Vector2(0.45f, 0.6f), parentFrame.RectTransform,
Anchor.Center), text)
3415 OnClicked = (button, data) =>
3418 ResetParamsEditor();
3424 void CreateAddButton(SerializableEntityEditor editor, Action onButtonClicked, LocalizedString text)
3426 if (editor ==
null) {
return; }
3427 var parent =
new GUIFrame(
new RectTransform(
new Point(editor.Rect.Width, (
int)(60 * GUI.yScale)), editor.RectTransform), style:
null)
3429 CanBeFocused =
false
3431 new GUIButton(
new RectTransform(
new Vector2(0.45f, 0.4f), parent.RectTransform,
Anchor.CenterLeft), text)
3433 OnClicked = (button, data) =>
3436 ResetParamsEditor();
3440 editor.AddCustomContent(parent, editor.ContentCount);
3444 private void TryUpdateAnimParam(
string name,
object value) => TryUpdateAnimParam(name.ToIdentifier(), value);
3445 private void TryUpdateAnimParam(Identifier name,
object value) => TryUpdateParam(character.AnimController.CurrentAnimationParams, name, value);
3446 private void TryUpdateRagdollParam(
string name,
object value) => TryUpdateRagdollParam(name.ToIdentifier(), value);
3447 private void TryUpdateRagdollParam(Identifier name,
object value) => TryUpdateParam(RagdollParams, name, value);
3449 private void TryUpdateParam(EditableParams editableParams, Identifier name,
object value)
3451 if (editableParams.SerializableEntityEditor ==
null)
3453 editableParams.AddToEditor(ParamsEditor.Instance);
3455 if (editableParams.SerializableProperties.TryGetValue(name, out SerializableProperty p))
3457 editableParams.SerializableEntityEditor.UpdateValue(p, value);
3461 private void TryUpdateJointParam(LimbJoint joint,
string name,
object value) => TryUpdateJointParam(joint, name.ToIdentifier(), value);
3462 private void TryUpdateJointParam(LimbJoint joint, Identifier name,
object value) => TryUpdateSubParam(joint.Params, name, value);
3463 private void TryUpdateLimbParam(Limb limb,
string name,
object value) => TryUpdateLimbParam(limb, name.ToIdentifier(), value);
3464 private void TryUpdateLimbParam(Limb limb, Identifier name,
object value) => TryUpdateSubParam(limb.Params, name, value);
3466 private void TryUpdateSubParam(RagdollParams.SubParam ragdollSubParams, Identifier name,
object value)
3468 if (ragdollSubParams.SerializableEntityEditor ==
null)
3470 ragdollSubParams.AddToEditor(ParamsEditor.Instance);
3472 if (ragdollSubParams.SerializableProperties.TryGetValue(name, out SerializableProperty p))
3474 ragdollSubParams.SerializableEntityEditor.UpdateValue(p, value);
3478 var subParams = ragdollSubParams.SubParams.Where(sp => sp.SerializableProperties.ContainsKey(name)).FirstOrDefault();
3479 if (subParams !=
null)
3481 if (subParams.SerializableProperties.TryGetValue(name, out p))
3483 if (subParams.SerializableEntityEditor ==
null)
3485 subParams.AddToEditor(ParamsEditor.Instance);
3487 subParams.SerializableEntityEditor.UpdateValue(p, value);
3492 DebugConsole.ThrowErrorLocalized(GetCharacterEditorTranslation(
"NoFieldForParameterFound").Replace(
"[parameter]", name.Value));
3499 private Vector2 ScreenToSim(
float x,
float y) => ScreenToSim(
new Vector2(x, y));
3500 private Vector2 ScreenToSim(Vector2 p) => ConvertUnits.ToSimUnits(Cam.ScreenToWorld(p)) +
Submarine.MainSub.SimPosition;
3501 private Vector2 SimToScreen(
float x,
float y) => SimToScreen(
new Vector2(x, y));
3502 private Vector2 SimToScreen(Vector2 p) => Cam.WorldToScreen(ConvertUnits.ToDisplayUnits(p +
Submarine.MainSub.SimPosition));
3504 private bool IsMatchingLimb(Limb limb1, Limb limb2, LimbJoint joint1, LimbJoint joint2) =>
3505 joint1.BodyA == limb1.body.FarseerBody && joint2.BodyA == limb2.body.FarseerBody ||
3506 joint1.BodyB == limb1.body.FarseerBody && joint2.BodyB == limb2.body.FarseerBody;
3508 private void ValidateJoint(LimbJoint limbJoint)
3510 if (limbJoint.UpperLimit < limbJoint.LowerLimit)
3512 if (limbJoint.LowerLimit > 0.0f)
3514 limbJoint.LowerLimit -= MathHelper.TwoPi;
3516 if (limbJoint.UpperLimit < 0.0f)
3518 limbJoint.UpperLimit += MathHelper.TwoPi;
3521 limbJoint.LowerLimit = MathUtils.WrapAnglePi(limbJoint.LowerLimit);
3522 limbJoint.UpperLimit = MathUtils.WrapAnglePi(limbJoint.UpperLimit);
3525 private Limb GetClosestLimbOnRagdoll(Vector2 targetPos, Func<Limb, bool> filter =
null)
3527 Limb closestLimb =
null;
3528 float closestDistance =
float.MaxValue;
3529 foreach (Limb l
in character.AnimController.Limbs)
3531 if (filter ==
null ?
true : filter(l))
3533 float distance = Vector2.DistanceSquared(SimToScreen(l.SimPosition), targetPos);
3534 if (distance < closestDistance)
3537 closestDistance = distance;
3544 private Limb GetClosestLimbOnSpritesheet(Vector2 targetPos, Func<Limb, bool> filter =
null)
3546 Limb closestLimb =
null;
3547 float closestDistance =
float.MaxValue;
3548 foreach (Limb l
in character.AnimController.Limbs)
3550 if (l ==
null) {
continue; }
3551 if (filter ==
null ?
true : filter(l))
3553 float distance = Vector2.DistanceSquared(GetLimbSpritesheetRect(l).
Center.ToVector2(), targetPos);
3554 if (distance < closestDistance)
3557 closestDistance = distance;
3564 private Rectangle GetLimbSpritesheetRect(Limb limb)
3566 int offsetX = spriteSheetOffsetX;
3567 int offsetY = spriteSheetOffsetY;
3569 for (
int i = 0; i < Textures.Count; i++)
3571 if (limb.ActiveSprite.FilePath != texturePaths[i])
3573 offsetY += (int)(Textures[i].Height * spriteSheetZoom);
3577 rect = limb.ActiveSprite.SourceRect;
3578 rect.Size = rect.MultiplySize(spriteSheetZoom);
3579 rect.Location = rect.Location.Multiply(spriteSheetZoom);
3588 private void UpdateSourceRect(Limb limb, Rectangle newRect,
bool resize)
3590 Sprite activeSprite = limb.ActiveSprite;
3591 activeSprite.SourceRect = newRect;
3592 if (limb.DamagedSprite !=
null)
3594 limb.DamagedSprite.SourceRect = activeSprite.SourceRect;
3596 Vector2 colliderSize =
new Vector2(ConvertUnits.ToSimUnits(newRect.Width), ConvertUnits.ToSimUnits(newRect.Height));
3599 if (recalculateCollider)
3601 RecalculateCollider(limb, colliderSize);
3604 var spritePos =
new Vector2(spriteSheetOffsetX, GetOffsetY(activeSprite));
3605 var originWidget = GetLimbEditWidget($
"{limb.Params.ID}_origin", limb);
3606 if (!resize && originWidget !=
null)
3608 Vector2 newOrigin = (originWidget.DrawPos - spritePos - activeSprite.SourceRect.Location.ToVector2() * spriteSheetZoom) / spriteSheetZoom;
3609 RecalculateOrigin(limb, newOrigin);
3613 RecalculateOrigin(limb);
3615 TryUpdateLimbParam(limb,
"sourcerect", newRect);
3616 if (limbPairEditing)
3618 UpdateOtherLimbs(limb, otherLimb =>
3620 otherLimb.ActiveSprite.SourceRect = newRect;
3621 if (otherLimb.DamagedSprite !=
null)
3623 otherLimb.DamagedSprite.SourceRect = newRect;
3627 if (recalculateCollider)
3629 RecalculateCollider(otherLimb, colliderSize);
3632 if (!resize && originWidget !=
null)
3634 Vector2 newOrigin = (originWidget.DrawPos - spritePos - activeSprite.SourceRect.Location.ToVector2() * spriteSheetZoom) / spriteSheetZoom;
3635 RecalculateOrigin(otherLimb, newOrigin);
3639 RecalculateOrigin(otherLimb);
3641 TryUpdateLimbParam(otherLimb,
"sourcerect", newRect);
3646 private void CalculateSpritesheetZoom()
3648 var texture = textures.OrderByDescending(t => t.Width).FirstOrDefault();
3649 if (texture ==
null)
3651 spriteSheetZoom = 1;
3654 float width = texture.Width;
3655 float height = textures.Sum(t => t.Height);
3657 if (unrestrictSpritesheet)
3659 spriteSheetMaxZoom = (GameMain.GraphicsWidth - spriteSheetOffsetX * 2 - margin - leftArea.Rect.Width) / width;
3665 spriteSheetMaxZoom = (centerArea.Rect.Bottom - spriteSheetOffsetY - margin) / height;
3669 spriteSheetMaxZoom = (centerArea.Rect.Left - spriteSheetOffsetX - margin) / width;
3672 spriteSheetMinZoom = spriteSheetMinZoom > spriteSheetMaxZoom ? spriteSheetMaxZoom : 0.25f;
3673 spriteSheetZoom = MathHelper.Clamp(1, spriteSheetMinZoom, spriteSheetMaxZoom);
3676 private void HandleLimbSelection(Limb limb)
3680 SetToggle(limbsToggle,
true);
3682 if (!selectedLimbs.Contains(limb))
3684 if (!Widget.EnableMultiSelect)
3686 selectedLimbs.Clear();
3688 selectedLimbs.Add(limb);
3689 ResetParamsEditor();
3692 else if (Widget.EnableMultiSelect)
3694 selectedLimbs.Remove(limb);
3695 ResetParamsEditor();
3699 private void OpenDoors()
3701 foreach (var item
in Item.ItemList)
3703 foreach (var component
in item.Components)
3705 if (component is Items.Components.Door door)
3713 private void SaveSnapshot()
3715 if (editJoints || editLimbs || editIK)
3717 RagdollParams.StoreSnapshot();
3721 CurrentAnimation.StoreSnapshot();
3725 private void ToggleJointCreationMode()
3727 switch (jointCreationMode)
3729 case JointCreationMode.None:
3730 jointCreationMode = JointCreationMode.Select;
3731 SetToggle(spritesheetToggle,
true);
3733 case JointCreationMode.Select:
3734 case JointCreationMode.Create:
3735 jointCreationMode = JointCreationMode.None;
3740 private void ToggleLimbCreationMode()
3742 isDrawingLimb = !isDrawingLimb;
3745 SetToggle(spritesheetToggle,
true);
3750 #region Animation Controls
3751 private void DrawAnimationControls(SpriteBatch spriteBatch,
float deltaTime)
3753 var collider = character.AnimController.Collider;
3754 var colliderDrawPos = cam.
WorldToScreen(collider.DrawPosition);
3755 var animParams = character.AnimController.CurrentAnimationParams;
3756 var groundedParams = animParams as GroundedMovementParams;
3757 var humanParams = animParams as IHumanAnimation;
3758 var humanGroundedParams = animParams as HumanGroundedParams;
3759 var humanSwimParams = animParams as HumanSwimParams;
3760 var fishParams = animParams as IFishAnimation;
3761 var fishGroundedParams = animParams as FishGroundedParams;
3762 var fishSwimParams = animParams as FishSwimParams;
3763 var head = character.AnimController.GetLimb(
LimbType.Head);
3764 var torso = character.AnimController.GetLimb(
LimbType.Torso);
3765 var tail = character.AnimController.GetLimb(
LimbType.Tail);
3766 var legs = character.AnimController.GetLimb(
LimbType.Legs);
3767 var thigh = character.AnimController.GetLimb(
LimbType.RightThigh) ?? character.AnimController.GetLimb(
LimbType.LeftThigh);
3768 var foot = character.AnimController.GetLimb(
LimbType.RightFoot) ?? character.AnimController.GetLimb(
LimbType.LeftFoot);
3769 var hand = character.AnimController.GetLimb(
LimbType.RightHand) ?? character.AnimController.GetLimb(
LimbType.LeftHand);
3770 var arm = character.AnimController.GetLimb(
LimbType.RightArm) ?? character.AnimController.GetLimb(
LimbType.LeftArm);
3772 float dir = character.AnimController.Dir;
3773 Vector2 GetSimSpaceForward() => animParams.IsSwimAnimation ? Vector2.Transform(Vector2.UnitY, Matrix.CreateRotationZ(collider.Rotation)) : Vector2.UnitX * character.AnimController.Dir;
3774 Vector2 GetScreenSpaceForward() => animParams.IsSwimAnimation ? VectorExtensions.BackwardFlipped(collider.Rotation, 1) : Vector2.UnitX * character.AnimController.Dir;
3775 bool ShowCycleWidget() => PlayerInput.KeyDown(Keys.LeftAlt) && (CurrentAnimation is IHumanAnimation || CurrentAnimation is GroundedMovementParams);
3776 if (!PlayerInput.KeyDown(Keys.LeftAlt) && (animParams is IHumanAnimation || animParams is GroundedMovementParams))
3778 GUI.DrawString(spriteBatch,
new Vector2(GameMain.GraphicsWidth / 2 - 120, 150), GetCharacterEditorTranslation(
"HoldLeftAltToAdjustCycleSpeed"), Color.White, Color.Black * 0.5f, 10, GUIStyle.Font);
3781 Vector2 referencePoint = cam.
WorldToScreen(head !=
null ? head.DrawPosition: collider.DrawPosition);
3782 Vector2 drawPos = referencePoint;
3783 if (ShowCycleWidget())
3785 GetAnimationWidget(
"CycleSpeed", Color.MediumPurple, Color.Black, size: 20, sizeMultiplier: 1.5f, shape:
WidgetShape.Circle, initMethod: w =>
3787 float multiplier = 0.5f;
3788 w.Tooltip = GetCharacterEditorTranslation(
"CycleSpeed");
3791 var refPoint = cam.WorldToScreen(head != null ? head.DrawPosition : collider.DrawPosition);
3792 w.DrawPos = refPoint + GetScreenSpaceForward() * ConvertUnits.ToDisplayUnits(CurrentAnimation.CycleSpeed * multiplier) * Cam.Zoom;
3794 w.Tooltip = $
"{GetCharacterEditorTranslation("CycleSpeed
")}: {CurrentAnimation.CycleSpeed.FormatDoubleDecimal()}";
3796 w.MouseHeld += dTime =>
3801 float speed = CurrentAnimation.CycleSpeed + ConvertUnits.ToSimUnits(Vector2.Multiply(PlayerInput.MouseSpeed / multiplier, GetScreenSpaceForward()).Combine()) / Cam.Zoom;
3802 TryUpdateAnimParam(
"cyclespeed", speed);
3803 w.Tooltip = $
"{GetCharacterEditorTranslation("CycleSpeed
")}: {CurrentAnimation.CycleSpeed.FormatDoubleDecimal()}";
3806 w.PreUpdate += dTime =>
3808 if (!ShowCycleWidget())
3814 w.PreDraw += (sp, dTime) =>
3821 w.PostDraw += (sp, dTime) =>
3825 GUI.DrawLine(spriteBatch, w.DrawPos, cam.
WorldToScreen(head !=
null ? head.DrawPosition : collider.DrawPosition), Color.MediumPurple);
3828 }).Draw(spriteBatch, deltaTime);
3832 GetAnimationWidget(
"MovementSpeed", Color.Turquoise, Color.Black, size: 20, sizeMultiplier: 1.5f, shape:
WidgetShape.Circle, initMethod: w =>
3834 float multiplier = 0.5f;
3835 w.Tooltip = GetCharacterEditorTranslation(
"MovementSpeed");
3838 var refPoint = cam.WorldToScreen(head != null ? head.DrawPosition : collider.DrawPosition);
3839 w.DrawPos = refPoint + GetScreenSpaceForward() * ConvertUnits.ToDisplayUnits(CurrentAnimation.MovementSpeed * multiplier) * Cam.Zoom;
3841 w.MouseHeld += dTime =>
3846 float speed = CurrentAnimation.MovementSpeed + ConvertUnits.ToSimUnits(Vector2.Multiply(PlayerInput.MouseSpeed / multiplier, GetScreenSpaceForward()).Combine()) / Cam.Zoom;
3847 TryUpdateAnimParam(
"movementspeed", MathHelper.Clamp(speed, 0.1f, Ragdoll.MAX_SPEED));
3849 if (humanSwimParams != null)
3851 TryUpdateAnimParam(
"cyclespeed", character.AnimController.CurrentAnimationParams.MovementSpeed);
3853 w.Tooltip = $
"{GetCharacterEditorTranslation("MovementSpeed
")}: {CurrentAnimation.MovementSpeed.FormatSingleDecimal()}";
3856 w.PreUpdate += dTime =>
3858 if (ShowCycleWidget())
3864 w.PreDraw += (sp, dTime) =>
3871 w.PostDraw += (sp, dTime) =>
3875 GUI.DrawLine(spriteBatch, w.DrawPos, Cam.WorldToScreen(head !=
null ? head.DrawPosition : collider.DrawPosition), Color.Turquoise);
3878 }).Draw(spriteBatch, deltaTime);
3884 DrawRadialWidget(spriteBatch, Cam.WorldToScreen(head.DrawPosition), animParams.HeadAngle, GetCharacterEditorTranslation(
"HeadAngle"), Color.White,
3885 angle => TryUpdateAnimParam(
"headangle", angle), circleRadius: 25, rotationOffset: -collider.Rotation + head.Params.GetSpriteOrientation() * dir, clockWise: dir < 0, wrapAnglePi:
true, holdPosition:
true);
3887 Color color = GUIStyle.Red;
3888 if (animParams.IsGroundedAnimation)
3890 if (humanGroundedParams !=
null && character.AnimController is HumanoidAnimController humanAnimController)
3892 GetAnimationWidget(
"HeadPosition", color, Color.Black, initMethod: w =>
3894 w.Tooltip = GetCharacterEditorTranslation(
"Head");
3895 w.Refresh = () => w.DrawPos = Cam.WorldToScreen(
3897 head.DrawPosition.X + ConvertUnits.ToDisplayUnits(humanAnimController.HeadLeanAmount * character.AnimController.Dir),
3898 ConvertUnits.ToDisplayUnits(head.PullJointWorldAnchorB.Y)));
3899 bool isHorizontal = false;
3900 bool isDirectionSet = false;
3901 w.MouseDown += () => isDirectionSet = false;
3902 w.MouseHeld += dTime =>
3904 if (PlayerInput.MouseSpeed.NearlyEquals(Vector2.Zero)) { return; }
3905 if (!isDirectionSet)
3907 isHorizontal = Math.Abs(PlayerInput.MouseSpeed.X) > Math.Abs(PlayerInput.MouseSpeed.Y);
3908 isDirectionSet = true;
3910 var scaledInput = ConvertUnits.ToSimUnits(PlayerInput.MouseSpeed) / Cam.Zoom;
3911 if (PlayerInput.KeyDown(Keys.LeftAlt))
3915 TryUpdateAnimParam(
"headleanamount", humanGroundedParams.HeadLeanAmount + scaledInput.X * character.AnimController.Dir);
3917 w.DrawPos = new Vector2(PlayerInput.MousePosition.X, w.DrawPos.Y);
3921 TryUpdateAnimParam(
"headposition", humanGroundedParams.HeadPosition - scaledInput.Y / RagdollParams.JointScale);
3923 w.DrawPos = new Vector2(w.DrawPos.X, PlayerInput.MousePosition.Y);
3928 TryUpdateAnimParam(
"headleanamount", humanGroundedParams.HeadLeanAmount + scaledInput.X * character.AnimController.Dir);
3930 w.DrawPos = new Vector2(PlayerInput.MousePosition.X, w.DrawPos.Y);
3931 TryUpdateAnimParam(
"headposition", humanGroundedParams.HeadPosition - scaledInput.Y / RagdollParams.JointScale);
3933 w.DrawPos = new Vector2(w.DrawPos.X, PlayerInput.MousePosition.Y);
3936 w.PostDraw += (sB, dTime) =>
3938 if (w.IsControlled && isDirectionSet)
3940 if (PlayerInput.KeyDown(Keys.LeftAlt))
3944 GUI.DrawLine(spriteBatch, new Vector2(0, w.DrawPos.Y), new Vector2(GameMain.GraphicsWidth, w.DrawPos.Y), color);
3948 GUI.DrawLine(spriteBatch, new Vector2(w.DrawPos.X, 0), new Vector2(w.DrawPos.X, GameMain.GraphicsHeight), color);
3953 GUI.DrawLine(spriteBatch, new Vector2(0, w.DrawPos.Y), new Vector2(GameMain.GraphicsWidth, w.DrawPos.Y), color);
3954 GUI.DrawLine(spriteBatch, new Vector2(w.DrawPos.X, 0), new Vector2(w.DrawPos.X, GameMain.GraphicsHeight), color);
3957 else if (w.IsSelected)
3959 GUI.DrawLine(spriteBatch, w.DrawPos, cam.WorldToScreen(head.DrawPosition), color);
3962 }).Draw(spriteBatch, deltaTime);
3964 else if (groundedParams !=
null)
3966 GetAnimationWidget(
"HeadPosition", color, Color.Black, initMethod: w =>
3968 w.Tooltip = GetCharacterEditorTranslation(
"HeadPosition");
3969 w.Refresh = () => w.DrawPos = cam.WorldToScreen(new Vector2(head.DrawPosition.X, ConvertUnits.ToDisplayUnits(head.PullJointWorldAnchorB.Y)));
3970 w.MouseHeld += dTime =>
3972 w.DrawPos = cam.WorldToScreen(new Vector2(head.DrawPosition.X, ConvertUnits.ToDisplayUnits(head.PullJointWorldAnchorB.Y)));
3973 var scaledInput = ConvertUnits.ToSimUnits(PlayerInput.MouseSpeed) / Cam.Zoom / RagdollParams.JointScale;
3974 TryUpdateAnimParam(
"headposition", groundedParams.HeadPosition - scaledInput.Y);
3976 w.PostDraw += (sB, dTime) =>
3980 GUI.DrawLine(spriteBatch, new Vector2(w.DrawPos.X, 0), new Vector2(w.DrawPos.X, GameMain.GraphicsHeight), color);
3983 }).Draw(spriteBatch, deltaTime);
3989 referencePoint = torso.DrawPosition;
3990 if (animParams is HumanGroundedParams || animParams is HumanSwimParams)
3992 var f = Vector2.Transform(Vector2.UnitY, Matrix.CreateRotationZ(collider.Rotation));
3993 referencePoint -= f * 25f;
3996 DrawRadialWidget(spriteBatch, cam.
WorldToScreen(referencePoint), animParams.TorsoAngle, GetCharacterEditorTranslation(
"TorsoAngle"), Color.White,
3997 angle => TryUpdateAnimParam(
"torsoangle", angle), rotationOffset: -collider.Rotation + torso.Params.GetSpriteOrientation() * dir, clockWise: dir < 0, wrapAnglePi:
true, holdPosition:
true);
3998 Color color = Color.DodgerBlue;
3999 if (animParams.IsGroundedAnimation)
4002 if (humanGroundedParams !=
null && character.AnimController is HumanoidAnimController humanAnimController)
4004 GetAnimationWidget(
"TorsoPosition", color, Color.Black, initMethod: w =>
4006 w.Tooltip = GetCharacterEditorTranslation(
"Torso");
4007 w.Refresh = () => w.DrawPos = cam.WorldToScreen(
4008 new Vector2(torso.DrawPosition.X + ConvertUnits.ToDisplayUnits(humanAnimController.TorsoLeanAmount * character.AnimController.Dir),
4009 ConvertUnits.ToDisplayUnits(torso.PullJointWorldAnchorB.Y)));
4010 bool isHorizontal = false;
4011 bool isDirectionSet = false;
4012 w.MouseDown += () => isDirectionSet = false;
4013 w.MouseHeld += dTime =>
4015 if (PlayerInput.MouseSpeed.NearlyEquals(Vector2.Zero)) { return; }
4016 if (!isDirectionSet)
4018 isHorizontal = Math.Abs(PlayerInput.MouseSpeed.X) > Math.Abs(PlayerInput.MouseSpeed.Y);
4019 isDirectionSet = true;
4021 var scaledInput = ConvertUnits.ToSimUnits(PlayerInput.MouseSpeed) / Cam.Zoom;
4022 if (PlayerInput.KeyDown(Keys.LeftAlt))
4026 TryUpdateAnimParam(
"torsoleanamount", humanGroundedParams.TorsoLeanAmount + scaledInput.X * character.AnimController.Dir);
4028 w.DrawPos = new Vector2(PlayerInput.MousePosition.X, w.DrawPos.Y);
4032 TryUpdateAnimParam(
"torsoposition", humanGroundedParams.TorsoPosition - scaledInput.Y / RagdollParams.JointScale);
4034 w.DrawPos = new Vector2(w.DrawPos.X, PlayerInput.MousePosition.Y);
4039 TryUpdateAnimParam(
"torsoleanamount", humanGroundedParams.TorsoLeanAmount + scaledInput.X * character.AnimController.Dir);
4041 w.DrawPos = new Vector2(PlayerInput.MousePosition.X, w.DrawPos.Y);
4042 TryUpdateAnimParam(
"torsoposition", humanGroundedParams.TorsoPosition - scaledInput.Y / RagdollParams.JointScale);
4044 w.DrawPos = new Vector2(w.DrawPos.X, PlayerInput.MousePosition.Y);
4047 w.PostDraw += (sB, dTime) =>
4049 if (w.IsControlled && isDirectionSet)
4051 if (PlayerInput.KeyDown(Keys.LeftAlt))
4055 GUI.DrawLine(spriteBatch, new Vector2(0, w.DrawPos.Y), new Vector2(GameMain.GraphicsWidth, w.DrawPos.Y), color);
4059 GUI.DrawLine(spriteBatch, new Vector2(w.DrawPos.X, 0), new Vector2(w.DrawPos.X, GameMain.GraphicsHeight), color);
4064 GUI.DrawLine(spriteBatch, new Vector2(0, w.DrawPos.Y), new Vector2(GameMain.GraphicsWidth, w.DrawPos.Y), color);
4065 GUI.DrawLine(spriteBatch, new Vector2(w.DrawPos.X, 0), new Vector2(w.DrawPos.X, GameMain.GraphicsHeight), color);
4068 else if (w.IsSelected)
4070 GUI.DrawLine(spriteBatch, w.DrawPos, cam.WorldToScreen(torso.DrawPosition), color);
4073 }).Draw(spriteBatch, deltaTime);
4075 else if (groundedParams !=
null)
4077 GetAnimationWidget(
"TorsoPosition", color, Color.Black, initMethod: w =>
4079 w.Tooltip = GetCharacterEditorTranslation(
"TorsoPosition");
4080 w.Refresh = () => w.DrawPos = SimToScreen(torso.SimPosition.X, torso.PullJointWorldAnchorB.Y);
4081 w.MouseHeld += dTime =>
4083 w.DrawPos = SimToScreen(torso.SimPosition.X, torso.PullJointWorldAnchorB.Y);
4084 var scaledInput = ConvertUnits.ToSimUnits(PlayerInput.MouseSpeed) / Cam.Zoom / RagdollParams.JointScale;
4085 TryUpdateAnimParam(
"torsoposition", groundedParams.TorsoPosition - scaledInput.Y);
4087 w.PostDraw += (sB, dTime) =>
4091 GUI.DrawLine(spriteBatch, new Vector2(w.DrawPos.X, 0), new Vector2(w.DrawPos.X, GameMain.GraphicsHeight), color);
4094 }).Draw(spriteBatch, deltaTime);
4099 if (tail !=
null && fishParams !=
null)
4101 DrawRadialWidget(spriteBatch, cam.
WorldToScreen(tail.DrawPosition), fishParams.TailAngle, GetCharacterEditorTranslation(
"TailAngle"), Color.White,
4102 angle => TryUpdateAnimParam(
"tailangle", angle), circleRadius: 25, rotationOffset: -collider.Rotation + tail.Params.GetSpriteOrientation() * dir, clockWise: dir < 0, wrapAnglePi:
true, holdPosition:
true);
4107 if (fishParams !=
null)
4109 Vector2 colliderBottom = character.AnimController.GetColliderBottom();
4110 foreach (Limb limb
in character.AnimController.Limbs)
4112 if (limb.type !=
LimbType.LeftFoot && limb.type !=
LimbType.RightFoot)
continue;
4114 if (!fishParams.FootAnglesInRadians.ContainsKey(limb.Params.ID))
4116 fishParams.FootAnglesInRadians[limb.Params.ID] = 0.0f;
4119 DrawRadialWidget(spriteBatch,
4120 cam.
WorldToScreen(
new Vector2(limb.DrawPosition.X, ConvertUnits.ToDisplayUnits(colliderBottom.Y))),
4121 MathHelper.ToDegrees(fishParams.FootAnglesInRadians[limb.Params.ID]),
4122 GetCharacterEditorTranslation(
"FootAngle"), Color.White,
4125 fishParams.FootAnglesInRadians[limb.Params.ID] = MathHelper.ToRadians(angle);
4126 TryUpdateAnimParam(
"footangles", fishParams.FootAngles);
4128 circleRadius: 25, rotationOffset: -collider.Rotation + limb.Params.GetSpriteOrientation() * dir, clockWise: dir < 0, wrapAnglePi:
true, autoFreeze:
true);
4131 else if (humanParams !=
null)
4133 DrawRadialWidget(spriteBatch, cam.
WorldToScreen(foot.DrawPosition), humanParams.FootAngle, GetCharacterEditorTranslation(
"FootAngle"), Color.White,
4134 angle => TryUpdateAnimParam(
"footangle", angle), circleRadius: 25, rotationOffset: -collider.Rotation + foot.Params.GetSpriteOrientation() * dir, clockWise: dir > 0, wrapAnglePi:
true);
4137 if (groundedParams !=
null)
4139 GetAnimationWidget(
"StepSize", Color.LimeGreen, Color.Black, initMethod: w =>
4141 w.Tooltip = GetCharacterEditorTranslation(
"StepSize");
4144 var refPoint = cam.WorldToScreen(new Vector2(
4145 character.AnimController.Collider.DrawPosition.X,
4146 character.AnimController.GetColliderBottom().Y));
4147 var stepSize = ConvertUnits.ToDisplayUnits(character.AnimController.StepSize.Value);
4148 w.DrawPos = refPoint + new Vector2(stepSize.X * character.AnimController.Dir, -stepSize.Y) * Cam.Zoom;
4150 w.MouseHeld += dTime =>
4152 w.DrawPos = PlayerInput.MousePosition;
4153 var transformedInput = ConvertUnits.ToSimUnits(new Vector2(PlayerInput.MouseSpeed.X * character.AnimController.Dir, -PlayerInput.MouseSpeed.Y)) / Cam.Zoom / RagdollParams.JointScale;
4154 TryUpdateAnimParam(
"stepsize", groundedParams.StepSize + transformedInput);
4155 w.Tooltip = $
"{GetCharacterEditorTranslation("StepSize
")}: {groundedParams.StepSize.FormatDoubleDecimal()}";
4157 w.PostDraw += (sp, dTime) =>
4161 GUI.DrawLine(sp, w.DrawPos, SimToScreen(character.AnimController.GetColliderBottom()), Color.LimeGreen);
4164 }).Draw(spriteBatch, deltaTime);
4168 if (humanGroundedParams !=
null)
4170 if (hand !=
null || arm !=
null)
4172 GetAnimationWidget(
"HandMoveAmount", GUIStyle.Green, Color.Black, initMethod: w =>
4174 w.Tooltip = GetCharacterEditorTranslation(
"HandMoveAmount");
4178 var refPoint = cam.WorldToScreen(character.AnimController.Collider.DrawPosition + GetSimSpaceForward() * offset);
4179 var handMovement = ConvertUnits.ToDisplayUnits(humanGroundedParams.HandMoveAmount);
4180 w.DrawPos = refPoint + new Vector2(handMovement.X * character.AnimController.Dir, handMovement.Y) * Cam.Zoom;
4182 w.MouseHeld += dTime =>
4184 w.DrawPos = PlayerInput.MousePosition;
4185 var transformedInput = ConvertUnits.ToSimUnits(new Vector2(PlayerInput.MouseSpeed.X * character.AnimController.Dir, PlayerInput.MouseSpeed.Y) / Cam.Zoom);
4186 TryUpdateAnimParam(
"handmoveamount", humanGroundedParams.HandMoveAmount + transformedInput);
4187 w.Tooltip = $
"{GetCharacterEditorTranslation("HandMoveAmount
")}: {humanGroundedParams.HandMoveAmount.FormatDoubleDecimal()}";
4189 w.PostDraw += (sp, dTime) =>
4193 GUI.DrawLine(sp, w.DrawPos, cam.WorldToScreen(character.AnimController.Collider.DrawPosition + GetSimSpaceForward() * offset), GUIStyle.Green);
4196 }).Draw(spriteBatch, deltaTime);
4200 else if (tail !=
null && fishSwimParams !=
null)
4202 float amplitudeMultiplier = 20;
4203 float lengthMultiplier = 20;
4205 float GetAmplitude() => ConvertUnits.ToDisplayUnits(fishSwimParams.WaveAmplitude) * Cam.Zoom / amplitudeMultiplier;
4206 float GetWaveLength() => ConvertUnits.ToDisplayUnits(fishSwimParams.WaveLength) * Cam.Zoom / lengthMultiplier;
4207 Vector2 GetRefPoint() => cam.
WorldToScreen(collider.DrawPosition) - GetScreenSpaceForward() * ConvertUnits.ToDisplayUnits(collider.Radius) * 3 * Cam.Zoom;
4208 Vector2 GetDrawPos() => GetRefPoint() - GetScreenSpaceForward() * GetWaveLength();
4209 Vector2 GetDir() => GetRefPoint() - GetDrawPos();
4210 Vector2 GetStartPoint() => GetDrawPos() + GetDir() / 2;
4211 Vector2 GetControlPoint() => GetStartPoint() + GetScreenSpaceForward().Right() * character.AnimController.Dir * GetAmplitude();
4212 var lengthWidget = GetAnimationWidget(
"WaveLength", Color.NavajoWhite, Color.Black, size: 15, shape:
WidgetShape.Circle, initMethod: w =>
4214 w.Tooltip = GetCharacterEditorTranslation(
"TailMovementSpeed");
4215 w.Refresh = () => w.DrawPos = GetDrawPos();
4216 w.MouseHeld += dTime =>
4218 float input = Vector2.Multiply(ConvertUnits.ToSimUnits(PlayerInput.MouseSpeed), GetScreenSpaceForward()).Combine() / Cam.Zoom * lengthMultiplier;
4219 TryUpdateAnimParam(
"wavelength", MathHelper.Clamp(fishSwimParams.WaveLength - input, 0, 200));
4222 w.PreDraw += (sp, dTime) =>
4230 var amplitudeWidget = GetAnimationWidget(
"WaveAmplitude", Color.NavajoWhite, Color.Black, size: 15, shape:
WidgetShape.Circle, initMethod: w =>
4232 w.Tooltip = GetCharacterEditorTranslation(
"TailMovementAmount");
4233 w.Refresh = () => w.DrawPos = GetControlPoint();
4234 w.MouseHeld += dTime =>
4236 float input = Vector2.Multiply(ConvertUnits.ToSimUnits(PlayerInput.MouseSpeed), GetScreenSpaceForward().Right()).Combine() * character.AnimController.Dir / Cam.Zoom * amplitudeMultiplier;
4237 TryUpdateAnimParam(
"waveamplitude", MathHelper.Clamp(fishSwimParams.WaveAmplitude + input, -100, 100));
4240 w.PreDraw += (sp, dTime) =>
4248 if (lengthWidget.IsControlled || amplitudeWidget.IsControlled)
4250 GUI.DrawSineWithDots(spriteBatch, GetRefPoint(), -GetDir(), GetAmplitude(), GetWaveLength(), 5000, points, Color.NavajoWhite);
4252 lengthWidget.Draw(spriteBatch, deltaTime);
4253 amplitudeWidget.Draw(spriteBatch, deltaTime);
4256 else if (humanSwimParams !=
null)
4259 float amplitudeMultiplier = 5;
4260 float lengthMultiplier = 5;
4262 float GetAmplitude() => ConvertUnits.ToDisplayUnits(humanSwimParams.LegMoveAmount) * Cam.Zoom / amplitudeMultiplier;
4263 float GetWaveLength() => ConvertUnits.ToDisplayUnits(humanSwimParams.LegCycleLength) * Cam.Zoom / lengthMultiplier;
4264 Vector2 GetRefPoint() => cam.
WorldToScreen(character.DrawPosition - GetScreenSpaceForward().FlipY() * 75);
4265 Vector2 GetDrawPos() => GetRefPoint() - GetScreenSpaceForward() * GetWaveLength();
4266 Vector2 GetDir() => GetRefPoint() - GetDrawPos();
4267 Vector2 GetStartPoint() => GetDrawPos() + GetDir() / 2;
4268 Vector2 GetControlPoint() => GetStartPoint() + GetScreenSpaceForward().Right() * character.AnimController.Dir * GetAmplitude();
4269 var lengthWidget = GetAnimationWidget(
"LegMovementSpeed", Color.NavajoWhite, Color.Black, size: 15, shape:
WidgetShape.Circle, initMethod: w =>
4271 w.Tooltip = GetCharacterEditorTranslation(
"LegMovementSpeed");
4272 w.Refresh = () => w.DrawPos = GetDrawPos();
4273 w.MouseHeld += dTime =>
4275 float input = Vector2.Multiply(ConvertUnits.ToSimUnits(PlayerInput.MouseSpeed), GetScreenSpaceForward()).Combine() / Cam.Zoom * lengthMultiplier;
4276 TryUpdateAnimParam(
"legcyclelength", MathHelper.Clamp(humanSwimParams.LegCycleLength - input, 0, 20));
4279 w.PreDraw += (sp, dTime) =>
4287 var amplitudeWidget = GetAnimationWidget(
"LegMovementAmount", Color.NavajoWhite, Color.Black, size: 15, shape:
WidgetShape.Circle, initMethod: w =>
4289 w.Tooltip = GetCharacterEditorTranslation(
"LegMovementAmount");
4290 w.Refresh = () => w.DrawPos = GetControlPoint();
4291 w.MouseHeld += dTime =>
4293 float input = Vector2.Multiply(ConvertUnits.ToSimUnits(PlayerInput.MouseSpeed), GetScreenSpaceForward().Right()).Combine() * character.AnimController.Dir / Cam.Zoom * amplitudeMultiplier;
4294 TryUpdateAnimParam(
"legmoveamount", MathHelper.Clamp(humanSwimParams.LegMoveAmount + input, -2, 2));
4297 w.PreDraw += (sp, dTime) =>
4305 if (lengthWidget.IsControlled || amplitudeWidget.IsControlled)
4307 GUI.DrawSineWithDots(spriteBatch, GetRefPoint(), -GetDir(), GetAmplitude(), GetWaveLength(), 5000, points, Color.NavajoWhite);
4309 lengthWidget.Draw(spriteBatch, deltaTime);
4310 amplitudeWidget.Draw(spriteBatch, deltaTime);
4312 GetAnimationWidget(
"HandMoveAmount", GUIStyle.Green, Color.Black, initMethod: w =>
4314 w.Tooltip = GetCharacterEditorTranslation(
"HandMoveAmount");
4318 var refPoint = cam.WorldToScreen(collider.DrawPosition + GetSimSpaceForward() * offset);
4319 var handMovement = ConvertUnits.ToDisplayUnits(humanSwimParams.HandMoveAmount);
4320 w.DrawPos = refPoint + new Vector2(handMovement.X * character.AnimController.Dir, handMovement.Y) * Cam.Zoom;
4322 w.MouseHeld += dTime =>
4324 w.DrawPos = PlayerInput.MousePosition;
4325 Vector2 transformedInput = ConvertUnits.ToSimUnits(new Vector2(PlayerInput.MouseSpeed.X * character.AnimController.Dir, PlayerInput.MouseSpeed.Y)) / Cam.Zoom;
4326 Vector2 handMovement = humanSwimParams.HandMoveAmount + transformedInput;
4327 TryUpdateAnimParam(
"handmoveamount", handMovement);
4328 TryUpdateAnimParam(
"handcyclespeed", handMovement.X * 4);
4329 w.Tooltip = $
"{GetCharacterEditorTranslation("HandMoveAmount
")}: {humanSwimParams.HandMoveAmount.FormatDoubleDecimal()}";
4331 w.PostDraw += (sp, dTime) =>
4335 GUI.DrawLine(sp, w.DrawPos, cam.WorldToScreen(collider.DrawPosition + GetSimSpaceForward() * offset), GUIStyle.Green);
4338 }).Draw(spriteBatch, deltaTime);
4341 foreach (Limb limb
in character.AnimController.Limbs)
4345 GUI.DrawRectangle(spriteBatch, SimToScreen(limb.DebugRefPos) - Vector2.One * 3, Vector2.One * 6, Color.White, isFilled:
true);
4346 GUI.DrawRectangle(spriteBatch, SimToScreen(limb.DebugTargetPos) - Vector2.One * 3, Vector2.One * 6, GUIStyle.Green, isFilled:
true);
4353 private Vector2[] corners =
new Vector2[4];
4354 private Vector2[] GetLimbPhysicRect(Limb limb)
4356 Vector2 size = ConvertUnits.ToDisplayUnits(limb.body.GetSize()) * Cam.Zoom;
4357 Vector2 up = VectorExtensions.BackwardFlipped(limb.Rotation);
4358 Vector2 limbScreenPos = cam.
WorldToScreen(limb.DrawPosition);
4359 corners = MathUtils.GetImaginaryRect(corners, up, limbScreenPos, size);
4363 private void DrawLimbEditor(SpriteBatch spriteBatch)
4365 float inputMultiplier = 0.5f;
4366 foreach (Limb limb
in character.AnimController.Limbs)
4368 if (limb ==
null || limb.ActiveSprite ==
null) {
continue; }
4369 var origin = limb.ActiveSprite.Origin;
4370 var sourceRect = limb.ActiveSprite.SourceRect;
4371 Vector2 limbScreenPos = cam.
WorldToScreen(limb.DrawPosition);
4372 bool isSelected = selectedLimbs.Contains(limb);
4373 corners = GetLimbPhysicRect(limb);
4374 if (isSelected && jointStartLimb != limb && jointEndLimb != limb)
4376 GUI.DrawRectangle(spriteBatch, corners, Color.Yellow, thickness: 3);
4378 if (GUI.MouseOn ==
null && Widget.SelectedWidgets.None() && !spriteSheetRect.Contains(PlayerInput.MousePosition) && MathUtils.RectangleContainsPoint(corners, PlayerInput.MousePosition))
4383 if (!lockSpriteOrigin && PlayerInput.PrimaryMouseButtonHeld())
4385 Vector2 forward = Vector2.Transform(Vector2.UnitY, Matrix.CreateRotationZ(limb.Rotation));
4386 var input = -scaledMouseSpeed * inputMultiplier / Cam.Zoom / limb.Scale / limb.TextureScale;
4387 var sprite = limb.ActiveSprite;
4388 origin += input.TransformVector(forward);
4389 var max =
new Vector2(sourceRect.Width, sourceRect.Height);
4390 sprite.Origin = origin.Clamp(Vector2.Zero, max);
4391 if (limb.DamagedSprite !=
null)
4393 limb.DamagedSprite.Origin = sprite.Origin;
4395 if (character.AnimController.IsFlipped)
4397 origin.X = Math.Abs(origin.X - sourceRect.Width);
4399 TryUpdateLimbParam(limb,
"origin", limb.ActiveSprite.RelativeOrigin);
4400 if (limbPairEditing)
4402 UpdateOtherLimbs(limb, otherLimb =>
4404 otherLimb.ActiveSprite.Origin = sprite.Origin;
4405 if (otherLimb.DamagedSprite !=
null)
4407 otherLimb.DamagedSprite.Origin = sprite.Origin;
4409 TryUpdateLimbParam(otherLimb,
"origin", otherLimb.ActiveSprite.RelativeOrigin);
4412 GUI.DrawString(spriteBatch, limbScreenPos +
new Vector2(10, -10), limb.ActiveSprite.RelativeOrigin.FormatDoubleDecimal(), Color.Yellow, Color.Black * 0.5f);
4417 GUI.DrawRectangle(spriteBatch, corners, Color.White);
4418 GUI.DrawString(spriteBatch, limbScreenPos +
new Vector2(10, -10), limb.Name, Color.White, Color.Black * 0.5f);
4424 private void DrawRagdoll(SpriteBatch spriteBatch,
float deltaTime)
4426 bool altDown = PlayerInput.KeyDown(Keys.LeftAlt);
4428 if (!altDown && editJoints && selectedJoints.Any() && jointCreationMode == JointCreationMode.None)
4430 GUI.DrawString(spriteBatch,
new Vector2(GameMain.GraphicsWidth / 2 - 180, 100), GetCharacterEditorTranslation(
"HoldLeftAltToManipulateJoint"), Color.White, Color.Black * 0.5f, 10, GUIStyle.Font);
4433 foreach (Limb limb
in character.AnimController.Limbs)
4439 var pullJointWidgetSize =
new Vector2(5, 5);
4440 Vector2 tformedPullPos = SimToScreen(limb.PullJointWorldAnchorA) + limb.body.DrawPositionOffset;
4441 GUI.DrawRectangle(spriteBatch, tformedPullPos - pullJointWidgetSize / 2, pullJointWidgetSize, GUIStyle.Red,
true);
4442 DrawWidget(spriteBatch, tformedPullPos, WidgetType.Rectangle, 8, Color.Cyan, $
"IK ({limb.Name})", () =>
4444 if (!selectedLimbs.Contains(limb))
4446 selectedLimbs.Add(limb);
4447 ResetParamsEditor();
4449 limb.PullJointWorldAnchorA = ScreenToSim(PlayerInput.MousePosition);
4450 TryUpdateLimbParam(limb,
"pullpos", ConvertUnits.ToDisplayUnits(limb.PullJointLocalAnchorA / limb.Params.Scale / limb.Params.Ragdoll.LimbScale));
4451 GUI.DrawLine(spriteBatch, SimToScreen(limb.SimPosition), tformedPullPos, Color.MediumPurple);
4455 foreach (var joint
in character.AnimController.LimbJoints)
4457 Vector2 jointPos = Vector2.Zero;
4458 Vector2 otherPos = Vector2.Zero;
4459 Vector2 anchorPosA = ConvertUnits.ToDisplayUnits(joint.LocalAnchorA);
4460 Vector2 anchorPosB = ConvertUnits.ToDisplayUnits(joint.LocalAnchorB);
4461 if (joint.BodyA == limb.body.FarseerBody)
4463 jointPos = anchorPosA;
4464 otherPos = anchorPosB;
4466 else if (joint.BodyB == limb.body.FarseerBody)
4468 jointPos = anchorPosB;
4469 otherPos = anchorPosA;
4475 Vector2 limbScreenPos = cam.
WorldToScreen(limb.DrawPosition);
4476 var f = Vector2.Transform(jointPos, Matrix.CreateRotationZ(limb.Rotation));
4478 Vector2 tformedJointPos = limbScreenPos + f * Cam.Zoom;
4481 ShapeExtensions.DrawPoint(spriteBatch, limbScreenPos, Color.Black, size: 5);
4482 ShapeExtensions.DrawPoint(spriteBatch, limbScreenPos, Color.White, size: 1);
4483 GUI.DrawLine(spriteBatch, limbScreenPos, tformedJointPos, Color.Black, width: 3);
4484 GUI.DrawLine(spriteBatch, limbScreenPos, tformedJointPos, Color.White, width: 1);
4488 if (altDown && joint.BodyA == limb.body.FarseerBody)
4492 if (!altDown && joint.BodyB == limb.body.FarseerBody)
4496 var selectionWidget = GetJointSelectionWidget($
"{joint.Params.Name} selection widget ragdoll", joint);
4497 selectionWidget.DrawPos = tformedJointPos;
4498 selectionWidget.Draw(spriteBatch, deltaTime);
4499 if (selectedJoints.Contains(joint))
4501 if (joint.LimitEnabled && jointCreationMode == JointCreationMode.None)
4503 var otherBody = limb == joint.LimbA ? joint.LimbB : joint.LimbA;
4504 float rotation = -otherBody.Rotation + limb.Params.GetSpriteOrientation();
4505 if (character.AnimController.Dir < 0)
4507 rotation -= MathHelper.Pi;
4509 DrawJointLimitWidgets(spriteBatch, limb, joint, tformedJointPos, autoFreeze:
true, allowPairEditing:
true, rotationOffset: rotation, holdPosition:
true);
4511 Limb referenceLimb = altDown ? joint.LimbB : joint.LimbA;
4513 Vector2 to = tformedJointPos - VectorExtensions.ForwardFlipped(referenceLimb.Rotation - referenceLimb.Params.GetSpriteOrientation(), 150);
4514 GUI.DrawLine(spriteBatch, tformedJointPos, to, Color.LightGray * 0.7f, width: 2);
4515 var dotSize =
new Vector2(5, 5);
4516 var rect =
new Rectangle((tformedJointPos - dotSize / 2).ToPoint(), dotSize.ToPoint());
4522 string tooltip = $
"{joint.Params.Name} {jointPos.FormatZeroDecimal()}";
4523 GUI.DrawString(spriteBatch, tformedJointPos -
new Vector2(1.2f, 0.5f) * GUIStyle.Font.MeasureString(tooltip), tooltip, Color.White, Color.Black * 0.5f);
4524 if (PlayerInput.PrimaryMouseButtonHeld())
4526 if (!selectionWidget.IsControlled) {
continue; }
4527 if (jointCreationMode != JointCreationMode.None) {
continue; }
4534 character.AnimController.Collider.PhysEnabled =
false;
4536 Vector2 input = ConvertUnits.ToSimUnits(scaledMouseSpeed) / Cam.Zoom;
4538 input = input.TransformVector(VectorExtensions.ForwardFlipped(limb.Rotation));
4539 if (joint.BodyA == limb.body.FarseerBody)
4541 joint.LocalAnchorA += input;
4542 Vector2 transformedValue = ConvertUnits.ToDisplayUnits(joint.LocalAnchorA / joint.Scale);
4543 TryUpdateJointParam(joint,
"limb1anchor", transformedValue);
4545 if (copyJointSettings)
4547 foreach (var j
in selectedJoints)
4549 j.LocalAnchorA = joint.LocalAnchorA;
4550 TryUpdateJointParam(j,
"limb1anchor", transformedValue);
4554 else if (joint.BodyB == limb.body.FarseerBody)
4556 joint.LocalAnchorB += input;
4557 Vector2 transformedValue = ConvertUnits.ToDisplayUnits(joint.LocalAnchorB / joint.Scale);
4558 TryUpdateJointParam(joint,
"limb2anchor", transformedValue);
4560 if (copyJointSettings)
4562 foreach (var j
in selectedJoints)
4564 j.LocalAnchorB = joint.LocalAnchorB;
4565 TryUpdateJointParam(j,
"limb2anchor", transformedValue);
4570 if (limbPairEditing)
4572 UpdateOtherJoints(limb, (otherLimb, otherJoint) =>
4574 if (joint.BodyA == limb.body.FarseerBody && otherJoint.BodyA == otherLimb.body.FarseerBody)
4576 otherJoint.LocalAnchorA = joint.LocalAnchorA;
4577 TryUpdateJointParam(otherJoint,
"limb1anchor", ConvertUnits.ToDisplayUnits(joint.LocalAnchorA / joint.Scale));
4579 else if (joint.BodyB == limb.body.FarseerBody && otherJoint.BodyB == otherLimb.body.FarseerBody)
4581 otherJoint.LocalAnchorB = joint.LocalAnchorB;
4582 TryUpdateJointParam(otherJoint,
"limb2anchor", ConvertUnits.ToDisplayUnits(joint.LocalAnchorB / joint.Scale));
4589 isFrozen = freezeToggle.Selected;
4590 character.AnimController.Collider.PhysEnabled =
true;
4598 private void UpdateOtherLimbs(Limb limb, Action<Limb> updateAction)
4601 if (limbPairEditing)
4603 string limbType = limb.type.ToString();
4604 bool isLeft = limbType.Contains(
"Left");
4605 bool isRight = limbType.Contains(
"Right");
4606 if (isLeft || isRight)
4608 if (character.AnimController.HasMultipleLimbsOfSameType)
4610 GetOtherLimbs(limb)?.ForEach(l => UpdateOtherLimbs(l));
4614 Limb otherLimb = GetOtherLimb(limbType, isLeft);
4615 if (otherLimb !=
null)
4617 UpdateOtherLimbs(otherLimb);
4620 void UpdateOtherLimbs(Limb otherLimb)
4622 updateAction(otherLimb);
4628 private void UpdateOtherJoints(Limb limb, Action<Limb, LimbJoint> updateAction)
4631 if (limbPairEditing)
4633 string limbType = limb.type.ToString();
4634 bool isLeft = limbType.Contains(
"Left");
4635 bool isRight = limbType.Contains(
"Right");
4636 if (isLeft || isRight)
4638 if (character.AnimController.HasMultipleLimbsOfSameType)
4640 GetOtherLimbs(limb)?.ForEach(l => UpdateOtherJoints(l));
4644 Limb otherLimb = GetOtherLimb(limbType, isLeft);
4645 if (otherLimb !=
null)
4647 UpdateOtherJoints(otherLimb);
4650 void UpdateOtherJoints(Limb otherLimb)
4652 foreach (var otherJoint
in character.AnimController.LimbJoints)
4654 updateAction(otherLimb, otherJoint);
4661 private Limb GetOtherLimb(
string limbType,
bool isLeft)
4663 string otherLimbType = isLeft ? limbType.Replace(
"Left",
"Right") : limbType.Replace(
"Right",
"Left");
4664 if (Enum.TryParse(otherLimbType, out LimbType type))
4666 return character.AnimController.GetLimb(type);
4672 private IEnumerable<Limb> GetOtherLimbs(Limb limb)
4674 var otherLimbs = character.AnimController.Limbs.Where(l => l.type == limb.type && l != limb);
4675 string limbType = limb.type.ToString();
4676 string otherLimbType = limbType.Contains(
"Left") ? limbType.Replace(
"Left",
"Right") : limbType.Replace(
"Right",
"Left");
4677 if (Enum.TryParse(otherLimbType, out LimbType type))
4679 otherLimbs = otherLimbs.Union(character.AnimController.Limbs.Where(l => l.type == type));
4686 private List<Texture2D> textures;
4687 private List<Texture2D> Textures
4691 if (textures ==
null)
4698 private List<string> texturePaths;
4699 private void CreateTextures()
4701 textures =
new List<Texture2D>();
4702 texturePaths =
new List<string>();
4703 foreach (Limb limb
in character.AnimController.Limbs)
4705 if (limb.ActiveSprite ==
null || texturePaths.Contains(limb.ActiveSprite.FilePath.Value)) {
continue; }
4706 if (limb.ActiveSprite.Texture ==
null) {
continue; }
4707 textures.Add(limb.ActiveSprite.Texture);
4708 texturePaths.Add(limb.ActiveSprite.FilePath.Value);
4712 private void DrawSpritesheetEditor(SpriteBatch spriteBatch,
float deltaTime)
4714 int offsetX = spriteSheetOffsetX;
4715 int offsetY = spriteSheetOffsetY;
4716 for (
int i = 0; i < Textures.Count; i++)
4718 var texture = Textures[i];
4721 spriteBatch.Draw(texture,
4722 position:
new Vector2(offsetX, offsetY),
4724 origin: Vector2.Zero,
4725 sourceRectangle:
null,
4726 scale: spriteSheetZoom,
4727 effects: SpriteEffects.None,
4731 GUI.DrawRectangle(spriteBatch,
new Vector2(offsetX, offsetY), texture.Bounds.Size.ToVector2() * spriteSheetZoom, Color.White);
4732 foreach (Limb limb
in character.AnimController.Limbs)
4734 if (limb.ActiveSprite ==
null || limb.ActiveSprite.FilePath != texturePaths[i]) {
continue; }
4735 Rectangle rect = limb.ActiveSprite.SourceRect;
4736 rect.Size = rect.MultiplySize(spriteSheetZoom);
4737 rect.Location = rect.Location.Multiply(spriteSheetZoom);
4740 Vector2 origin = limb.ActiveSprite.Origin;
4741 Vector2 limbScreenPos =
new Vector2(rect.X + origin.X * spriteSheetZoom, rect.Y + origin.Y * spriteSheetZoom);
4743 foreach (var wearable
in limb.WearingItems)
4745 Vector2 orig = limb.ActiveSprite.Origin;
4746 if (!wearable.InheritOrigin)
4748 orig = wearable.Sprite.Origin;
4750 if (limb.body.Dir == -1.0f)
4752 orig.X = wearable.Sprite.SourceRect.Width - orig.X;
4755 spriteBatch.Draw(wearable.Sprite.Texture,
4756 position: limbScreenPos,
4759 sourceRectangle: wearable.InheritSourceRect ? limb.ActiveSprite.SourceRect : wearable.Sprite.SourceRect,
4760 scale: (wearable.InheritScale ? 1 : wearable.Scale / RagdollParams.TextureScale) * spriteSheetZoom,
4761 effects: SpriteEffects.None,
4766 if (character.AnimController.Dir < 0)
4768 limbScreenPos.X = rect.X + rect.Width - (float)Math.Round(origin.X * spriteSheetZoom);
4772 DrawSpritesheetJointEditor(spriteBatch, deltaTime, limb, limbScreenPos);
4774 bool isMouseOn = rect.Contains(PlayerInput.MousePosition);
4778 int halfSize = widgetSize / 2;
4779 Vector2 stringOffset =
new Vector2(5, 14);
4780 var topLeft = rect.Location.ToVector2();
4781 var topRight =
new Vector2(topLeft.X + rect.Width, topLeft.Y);
4782 var bottomRight =
new Vector2(topRight.X, topRight.Y + rect.Height);
4783 bool isSelected = selectedLimbs.Contains(limb);
4784 if (jointStartLimb != limb && jointEndLimb != limb)
4786 if (isSelected || !onlyShowSourceRectForSelectedLimbs)
4788 GUI.DrawRectangle(spriteBatch, rect, isSelected ? Color.Yellow : (isMouseOn ? Color.White : GUIStyle.Red));
4793 var sprite = limb.ActiveSprite;
4794 Vector2 GetTopLeft() => sprite.SourceRect.Location.ToVector2();
4795 Vector2 GetTopRight() =>
new Vector2(GetTopLeft().X + sprite.SourceRect.Width, GetTopLeft().Y);
4796 Vector2 GetBottomRight() =>
new Vector2(GetTopRight().X, GetTopRight().Y + sprite.SourceRect.Height);
4797 var originWidget = GetLimbEditWidget($
"{limb.Params.ID}_origin", limb, widgetSize,
WidgetShape.Cross, initMethod: w =>
4799 w.Refresh = () => w.Tooltip = $
"{GetCharacterEditorTranslation("Origin
")}: {sprite.RelativeOrigin.FormatDoubleDecimal()}";
4801 w.MouseHeld += dTime =>
4803 var spritePos = new Vector2(spriteSheetOffsetX, GetOffsetY(limb.ActiveSprite));
4804 w.DrawPos = PlayerInput.MousePosition.Clamp(spritePos + GetTopLeft() * spriteSheetZoom, spritePos + GetBottomRight() * spriteSheetZoom);
4805 sprite.Origin = (w.DrawPos - spritePos - sprite.SourceRect.Location.ToVector2() * spriteSheetZoom) / spriteSheetZoom;
4806 if (limb.DamagedSprite != null)
4808 limb.DamagedSprite.RelativeOrigin = sprite.RelativeOrigin;
4810 TryUpdateLimbParam(limb,
"origin", sprite.RelativeOrigin);
4811 if (limbPairEditing)
4813 UpdateOtherLimbs(limb, otherLimb =>
4815 otherLimb.ActiveSprite.RelativeOrigin = sprite.RelativeOrigin;
4816 if (otherLimb.DamagedSprite != null)
4818 otherLimb.DamagedSprite.RelativeOrigin = sprite.RelativeOrigin;
4820 TryUpdateLimbParam(otherLimb,
"origin", sprite.RelativeOrigin);
4824 w.PreUpdate += dTime =>
4829 w.Enabled = !lockSpriteOrigin;
4832 w.PreDraw += (sb, dTime) =>
4834 var spritePos =
new Vector2(spriteSheetOffsetX, GetOffsetY(limb.ActiveSprite));
4835 w.DrawPos = (spritePos + (sprite.Origin + sprite.SourceRect.Location.ToVector2()) * spriteSheetZoom)
4836 .Clamp(spritePos + GetTopLeft() * spriteSheetZoom, spritePos + GetBottomRight() * spriteSheetZoom);
4840 originWidget.Draw(spriteBatch, deltaTime);
4841 if (!lockSpritePosition && (limb.type !=
LimbType.Head || !character.IsHuman))
4843 var positionWidget = GetLimbEditWidget($
"{limb.Params.ID}_position", limb, widgetSize,
WidgetShape.Rectangle, initMethod: w =>
4845 w.Refresh = () => w.Tooltip = $
"{GetCharacterEditorTranslation("Position
")}: {limb.ActiveSprite.SourceRect.Location}";
4847 w.MouseHeld += dTime =>
4849 w.DrawPos = PlayerInput.MousePosition;
4850 Sprite activeSprite = limb.ActiveSprite;
4851 var newRect = activeSprite.SourceRect;
4852 newRect.Location = new Point(
4853 (int)((PlayerInput.MousePosition.X + halfSize - spriteSheetOffsetX) / spriteSheetZoom),
4854 (int)((PlayerInput.MousePosition.Y + halfSize - GetOffsetY(activeSprite)) / spriteSheetZoom));
4855 activeSprite.SourceRect = newRect;
4856 if (limb.DamagedSprite != null)
4858 limb.DamagedSprite.SourceRect = activeSprite.SourceRect;
4860 TryUpdateLimbParam(limb,
"sourcerect", newRect);
4861 var spritePos = new Vector2(spriteSheetOffsetX, GetOffsetY(activeSprite));
4862 Vector2 newOrigin = (originWidget.DrawPos - spritePos - activeSprite.SourceRect.Location.ToVector2() * spriteSheetZoom) / spriteSheetZoom;
4863 RecalculateOrigin(limb, newOrigin);
4864 if (limbPairEditing)
4866 UpdateOtherLimbs(limb, otherLimb =>
4868 otherLimb.ActiveSprite.SourceRect = newRect;
4869 if (otherLimb.DamagedSprite != null)
4871 otherLimb.DamagedSprite.SourceRect = newRect;
4873 TryUpdateLimbParam(otherLimb,
"sourcerect", newRect);
4874 RecalculateOrigin(otherLimb, newOrigin);
4878 w.PreDraw += (sb, dTime) => w.Refresh();
4880 if (!positionWidget.IsControlled)
4882 positionWidget.DrawPos = topLeft -
new Vector2(halfSize);
4884 positionWidget.Draw(spriteBatch, deltaTime);
4886 if (!lockSpriteSize && (limb.type !=
LimbType.Head || !character.IsHuman))
4888 var sizeWidget = GetLimbEditWidget($
"{limb.Params.ID}_size", limb, widgetSize,
WidgetShape.Rectangle, initMethod: w =>
4890 w.Refresh = () => w.Tooltip = $
"{GetCharacterEditorTranslation("Size
")}: {limb.ActiveSprite.SourceRect.Size}";
4892 w.MouseHeld += dTime =>
4894 w.DrawPos = PlayerInput.MousePosition;
4895 Sprite activeSprite = limb.ActiveSprite;
4896 Rectangle newRect = activeSprite.SourceRect;
4897 float offset_y = activeSprite.SourceRect.Y * spriteSheetZoom + GetOffsetY(activeSprite);
4898 float offset_x = activeSprite.SourceRect.X * spriteSheetZoom + spriteSheetOffsetX;
4899 int width = (int)((PlayerInput.MousePosition.X - halfSize - offset_x) / spriteSheetZoom);
4900 int height = (int)((PlayerInput.MousePosition.Y - halfSize - offset_y) / spriteSheetZoom);
4901 newRect.Size = new Point(width, height);
4902 activeSprite.SourceRect = newRect;
4903 activeSprite.size = new Vector2(width, height);
4904 Vector2 colliderSize = new Vector2(ConvertUnits.ToSimUnits(width), ConvertUnits.ToSimUnits(height));
4905 if (recalculateCollider)
4907 RecalculateCollider(limb, colliderSize);
4909 RecalculateOrigin(limb);
4910 if (limb.DamagedSprite != null)
4912 limb.DamagedSprite.SourceRect = activeSprite.SourceRect;
4914 TryUpdateLimbParam(limb,
"sourcerect", newRect);
4915 if (limbPairEditing)
4917 UpdateOtherLimbs(limb, otherLimb =>
4919 otherLimb.ActiveSprite.SourceRect = newRect;
4920 RecalculateOrigin(otherLimb);
4921 if (recalculateCollider)
4923 RecalculateCollider(otherLimb, colliderSize);
4925 if (otherLimb.DamagedSprite != null)
4927 otherLimb.DamagedSprite.SourceRect = newRect;
4929 TryUpdateLimbParam(otherLimb,
"sourcerect", newRect);
4933 w.PreDraw += (sb, dTime) => w.Refresh();
4935 if (!sizeWidget.IsControlled)
4937 sizeWidget.DrawPos = bottomRight +
new Vector2(halfSize);
4939 sizeWidget.Draw(spriteBatch, deltaTime);
4942 else if (isMouseOn && GUI.MouseOn ==
null && Widget.SelectedWidgets.None())
4945 GUI.DrawString(spriteBatch, limbScreenPos +
new Vector2(10, -10), limb.Name, Color.White, Color.Black * 0.5f);
4950 GUI.DrawRectangle(spriteBatch, rect, isMouseOn ? Color.White : Color.Gray);
4951 if (isMouseOn && GUI.MouseOn ==
null && Widget.SelectedWidgets.None())
4954 GUI.DrawString(spriteBatch, limbScreenPos +
new Vector2(10, -10), limb.Name, Color.White, Color.Black * 0.5f);
4958 offsetY += (int)(texture.Height * spriteSheetZoom);
4962 private int GetTextureHeight(Sprite sprite)
4964 int textureIndex = Textures.IndexOf(sprite.Texture);
4966 foreach (var t
in Textures)
4968 if (Textures.IndexOf(t) < textureIndex)
4973 return (
int)(height * spriteSheetZoom);
4976 private int GetOffsetY(Sprite sprite) => spriteSheetOffsetY + GetTextureHeight(sprite);
4978 private void RecalculateCollider(Limb l, Vector2 size)
4981 float multiplier = 0.9f;
4982 l.body.SetSize(
new Vector2(size.X, size.Y) * l.Scale * RagdollParams.TextureScale * multiplier);
4983 TryUpdateLimbParam(l,
"radius", ConvertUnits.ToDisplayUnits(l.body.Radius / l.Params.Scale / RagdollParams.LimbScale / RagdollParams.TextureScale));
4984 TryUpdateLimbParam(l,
"width", ConvertUnits.ToDisplayUnits(l.body.Width / l.Params.Scale / RagdollParams.LimbScale / RagdollParams.TextureScale));
4985 TryUpdateLimbParam(l,
"height", ConvertUnits.ToDisplayUnits(l.body.Height / l.Params.Scale / RagdollParams.LimbScale / RagdollParams.TextureScale));
4988 private void RecalculateOrigin(Limb l, Vector2? newOrigin =
null)
4990 Sprite activeSprite = l.ActiveSprite;
4991 if (lockSpriteOrigin)
4994 activeSprite.Origin = newOrigin ?? activeSprite.Origin;
4995 TryUpdateLimbParam(l,
"origin", activeSprite.RelativeOrigin);
5000 activeSprite.RelativeOrigin = activeSprite.RelativeOrigin;
5004 private void DrawSpritesheetJointEditor(SpriteBatch spriteBatch,
float deltaTime, Limb limb, Vector2 limbScreenPos,
float spriteRotation = 0)
5006 foreach (var joint
in character.AnimController.LimbJoints)
5008 Vector2 jointPos = Vector2.Zero;
5009 Vector2 anchorPosA = ConvertUnits.ToDisplayUnits(joint.LocalAnchorA);
5010 Vector2 anchorPosB = ConvertUnits.ToDisplayUnits(joint.LocalAnchorB);
5013 if (joint.BodyA == limb.body.FarseerBody)
5015 jointPos = anchorPosA;
5019 else if (joint.BodyB == limb.body.FarseerBody)
5021 jointPos = anchorPosB;
5029 Vector2 tformedJointPos = jointPos = jointPos / joint.Scale / limb.TextureScale * spriteSheetZoom;
5030 tformedJointPos.Y = -tformedJointPos.Y;
5031 tformedJointPos.X *= character.AnimController.Dir;
5032 tformedJointPos += limbScreenPos;
5033 var jointSelectionWidget = GetJointSelectionWidget($
"{joint.Params.Name} selection widget {anchorID}", joint, $
"{joint.Params.Name} selection widget {otherID}");
5034 jointSelectionWidget.DrawPos = tformedJointPos;
5035 jointSelectionWidget.Draw(spriteBatch, deltaTime);
5036 var otherWidget = GetJointSelectionWidget($
"{joint.Params.Name} selection widget {otherID}", joint, $
"{joint.Params.Name} selection widget {anchorID}");
5037 if (anchorID ==
"2")
5039 bool isSelected = selectedJoints.Contains(joint);
5040 bool isHovered = jointSelectionWidget.IsSelected || otherWidget.IsSelected;
5041 if (isSelected || isHovered)
5043 GUI.DrawLine(spriteBatch, jointSelectionWidget.DrawPos, otherWidget.DrawPos, jointSelectionWidget.Color, width: 2);
5046 if (selectedJoints.Contains(joint))
5048 if (joint.LimitEnabled && jointCreationMode == JointCreationMode.None)
5050 DrawJointLimitWidgets(spriteBatch, limb, joint, tformedJointPos, autoFreeze:
false, allowPairEditing:
true, holdPosition:
false, rotationOffset: joint.LimbB.Params.GetSpriteOrientation());
5052 if (jointSelectionWidget.IsControlled)
5054 Vector2 input = ConvertUnits.ToSimUnits(scaledMouseSpeed);
5056 input.X *= character.AnimController.Dir;
5057 input *= joint.Scale * limb.TextureScale / spriteSheetZoom;
5058 if (joint.BodyA == limb.body.FarseerBody)
5060 joint.LocalAnchorA += input;
5061 Vector2 transformedValue = ConvertUnits.ToDisplayUnits(joint.LocalAnchorA / joint.Scale);
5062 TryUpdateJointParam(joint,
"limb1anchor", transformedValue);
5064 if (copyJointSettings)
5066 foreach (var j
in selectedJoints)
5068 j.LocalAnchorA = joint.LocalAnchorA;
5069 TryUpdateJointParam(j,
"limb1anchor", transformedValue);
5073 else if (joint.BodyB == limb.body.FarseerBody)
5075 joint.LocalAnchorB += input;
5076 Vector2 transformedValue = ConvertUnits.ToDisplayUnits(joint.LocalAnchorB / joint.Scale);
5077 TryUpdateJointParam(joint,
"limb2anchor", transformedValue);
5079 if (copyJointSettings)
5081 foreach (var j
in selectedJoints)
5083 j.LocalAnchorB = joint.LocalAnchorB;
5084 TryUpdateJointParam(j,
"limb2anchor", transformedValue);
5088 if (limbPairEditing)
5090 UpdateOtherJoints(limb, (otherLimb, otherJoint) =>
5092 if (joint.BodyA == limb.body.FarseerBody && otherJoint.BodyA == otherLimb.body.FarseerBody)
5094 otherJoint.LocalAnchorA = joint.LocalAnchorA;
5095 TryUpdateJointParam(otherJoint,
"limb1anchor", ConvertUnits.ToDisplayUnits(joint.LocalAnchorA / joint.Scale));
5097 else if (joint.BodyB == limb.body.FarseerBody && otherJoint.BodyB == otherLimb.body.FarseerBody)
5099 otherJoint.LocalAnchorB = joint.LocalAnchorB;
5100 TryUpdateJointParam(otherJoint,
"limb2anchor", ConvertUnits.ToDisplayUnits(joint.LocalAnchorB / joint.Scale));
5109 private void DrawJointLimitWidgets(SpriteBatch spriteBatch, Limb limb, LimbJoint joint, Vector2 drawPos,
bool autoFreeze,
bool allowPairEditing,
bool holdPosition,
float rotationOffset = 0)
5111 bool clockWise = joint.Params.ClockWiseRotation;
5112 Color angleColor = joint.UpperLimit - joint.LowerLimit > 0 ? GUIStyle.Green * 0.5f : GUIStyle.Red;
5113 DrawRadialWidget(spriteBatch, drawPos, MathHelper.ToDegrees(joint.UpperLimit), $
"{joint.Params.Name}: {GetCharacterEditorTranslation("UpperLimit
")}", Color.Cyan, angle =>
5115 joint.UpperLimit = MathHelper.ToRadians(angle);
5116 ValidateJoint(joint);
5117 angle = MathHelper.ToDegrees(joint.UpperLimit);
5118 TryUpdateJointParam(joint,
"upperlimit", angle);
5119 if (copyJointSettings)
5121 foreach (var j in selectedJoints)
5123 if (j.LimitEnabled != joint.LimitEnabled)
5125 j.LimitEnabled = joint.LimitEnabled;
5126 TryUpdateJointParam(j,
"limitenabled", j.LimitEnabled);
5128 j.UpperLimit = joint.UpperLimit;
5129 TryUpdateJointParam(j,
"upperlimit", angle);
5132 if (allowPairEditing && limbPairEditing)
5134 UpdateOtherJoints(limb, (otherLimb, otherJoint) =>
5136 if (IsMatchingLimb(limb, otherLimb, joint, otherJoint))
5138 if (otherJoint.LimitEnabled != joint.LimitEnabled)
5140 otherJoint.LimitEnabled = otherJoint.LimitEnabled;
5141 TryUpdateJointParam(otherJoint,
"limitenabled", otherJoint.LimitEnabled);
5143 otherJoint.UpperLimit = joint.UpperLimit;
5144 TryUpdateJointParam(otherJoint,
"upperlimit", angle);
5148 DrawAngle(20, angleColor, 4);
5149 DrawAngle(40, Color.Cyan);
5150 GUI.DrawString(spriteBatch, drawPos, angle.FormatZeroDecimal(), Color.Black, backgroundColor: Color.Cyan, font: GUIStyle.SmallFont);
5151 }, circleRadius: 40, rotationOffset: rotationOffset, displayAngle:
false, clockWise: clockWise, holdPosition: holdPosition);
5152 DrawRadialWidget(spriteBatch, drawPos, MathHelper.ToDegrees(joint.LowerLimit), $
"{joint.Params.Name}: {GetCharacterEditorTranslation("LowerLimit
")}", Color.Yellow, angle =>
5154 joint.LowerLimit = MathHelper.ToRadians(angle);
5155 ValidateJoint(joint);
5156 angle = MathHelper.ToDegrees(joint.LowerLimit);
5157 TryUpdateJointParam(joint,
"lowerlimit", angle);
5158 if (copyJointSettings)
5160 foreach (var j in selectedJoints)
5162 if (j.LimitEnabled != joint.LimitEnabled)
5164 j.LimitEnabled = joint.LimitEnabled;
5165 TryUpdateJointParam(j,
"limitenabled", j.LimitEnabled);
5167 j.LowerLimit = joint.LowerLimit;
5168 TryUpdateJointParam(j,
"lowerlimit", angle);
5171 if (allowPairEditing && limbPairEditing)
5173 UpdateOtherJoints(limb, (otherLimb, otherJoint) =>
5175 if (IsMatchingLimb(limb, otherLimb, joint, otherJoint))
5177 if (otherJoint.LimitEnabled != joint.LimitEnabled)
5179 otherJoint.LimitEnabled = otherJoint.LimitEnabled;
5180 TryUpdateJointParam(otherJoint,
"limitenabled", otherJoint.LimitEnabled);
5182 otherJoint.LowerLimit = joint.LowerLimit;
5183 TryUpdateJointParam(otherJoint,
"lowerlimit", angle);
5187 DrawAngle(20, angleColor, 4);
5188 DrawAngle(25, Color.Yellow);
5189 GUI.DrawString(spriteBatch, drawPos, angle.FormatZeroDecimal(), Color.Black, backgroundColor: Color.Yellow, font: GUIStyle.SmallFont);
5190 }, circleRadius: 25, rotationOffset: rotationOffset, displayAngle:
false, clockWise: clockWise, holdPosition: holdPosition);
5191 void DrawAngle(
float radius, Color color,
float thickness = 5)
5193 float angle = joint.UpperLimit - joint.LowerLimit;
5194 float offset = clockWise ? rotationOffset + joint.LowerLimit - MathHelper.PiOver2 : rotationOffset - joint.UpperLimit - MathHelper.PiOver2;
5195 ShapeExtensions.DrawSector(spriteBatch, drawPos, radius, angle, 40, color, offset: offset, thickness: thickness);
5199 private void Nudge(Keys key)
5204 foreach (var limb
in selectedLimbs)
5207 if (limb.type ==
LimbType.Head && character.IsHuman) {
continue; }
5208 var newRect = limb.ActiveSprite.SourceRect;
5209 bool resize = PlayerInput.KeyDown(Keys.LeftControl);
5212 if (lockSpriteSize) {
return; }
5217 if (lockSpritePosition) {
return; }
5220 UpdateSourceRect(limb, newRect, resize);
5224 foreach (var limb
in selectedLimbs)
5227 if (limb.type ==
LimbType.Head && character.IsHuman) {
continue; }
5228 var newRect = limb.ActiveSprite.SourceRect;
5229 bool resize = PlayerInput.KeyDown(Keys.LeftControl);
5232 if (lockSpriteSize) {
return; }
5237 if (lockSpritePosition) {
return; }
5240 UpdateSourceRect(limb, newRect, resize);
5244 foreach (var limb
in selectedLimbs)
5247 if (limb.type ==
LimbType.Head && character.IsHuman) {
continue; }
5248 var newRect = limb.ActiveSprite.SourceRect;
5249 bool resize = PlayerInput.KeyDown(Keys.LeftControl);
5252 if (lockSpriteSize) {
return; }
5257 if (lockSpritePosition) {
return; }
5260 UpdateSourceRect(limb, newRect, resize);
5264 foreach (var limb
in selectedLimbs)
5267 if (limb.type ==
LimbType.Head && character.IsHuman) {
continue; }
5268 var newRect = limb.ActiveSprite.SourceRect;
5269 bool resize = PlayerInput.KeyDown(Keys.LeftControl);
5272 if (lockSpriteSize) {
return; }
5277 if (lockSpritePosition) {
return; }
5280 UpdateSourceRect(limb, newRect, resize);
5284 RagdollParams.StoreSnapshot();
5287 private void SetSpritesheetRestriction(
bool value)
5289 unrestrictSpritesheet = value;
5290 CalculateSpritesheetZoom();
5291 spriteSheetZoomBar.BarScroll = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(spriteSheetMinZoom, spriteSheetMaxZoom, spriteSheetZoom));
5295 #region Widgets as methods
5296 private void DrawRadialWidget(SpriteBatch spriteBatch, Vector2 drawPos,
float value, LocalizedString toolTip, Color color, Action<float> onClick,
5297 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)
5300 if (!MathUtils.IsValid(angle))
5304 float drawAngle = clockWise ? angle : -angle;
5305 var widgetDrawPos = drawPos + VectorExtensions.Forward(MathHelper.ToRadians(drawAngle) + rotationOffset - MathHelper.PiOver2, circleRadius);
5306 GUI.DrawLine(spriteBatch, drawPos, widgetDrawPos, color);
5307 DrawWidget(spriteBatch, widgetDrawPos, WidgetType.Rectangle, widgetSize, color, toolTip, () =>
5309 GUI.DrawLine(spriteBatch, drawPos, widgetDrawPos, color, width: 3);
5310 ShapeExtensions.DrawCircle(spriteBatch, drawPos, circleRadius, 40, color, thickness: 1);
5311 Vector2 d = PlayerInput.MousePosition - drawPos;
5312 float newAngle = clockWise
5313 ? MathUtils.VectorToAngle(d) + MathHelper.PiOver2 - rotationOffset
5314 : -MathUtils.VectorToAngle(d) - MathHelper.PiOver2 + rotationOffset;
5315 angle = MathHelper.ToDegrees(wrapAnglePi ? MathUtils.WrapAnglePi(newAngle) : MathUtils.WrapAngleTwoPi(newAngle));
5316 angle = (float)Math.Round(angle / rounding) * rounding;
5317 if (angle >= 360 || angle <= -360) { angle = 0; }
5320 GUI.DrawString(spriteBatch, drawPos, angle.FormatZeroDecimal(), Color.Black, backgroundColor: color, font: GUIStyle.SmallFont);
5323 }, autoFreeze, holdPosition, onHovered: () =>
5325 if (!PlayerInput.PrimaryMouseButtonHeld())
5327 GUIComponent.DrawToolTip(
5329 $
"{toolTip} ({angle.FormatZeroDecimal()})",
5330 new Vector2(drawPos.X + 50, drawPos.Y - widgetSize / 2 - 50));
5336 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)
5338 var drawRect =
new Rectangle((
int)drawPos.X - size / 2, (
int)drawPos.Y - size / 2, size, size);
5339 var inputRect = drawRect;
5340 inputRect.Inflate(size * 0.75f, size * 0.75f);
5341 bool isMouseOn = inputRect.Contains(PlayerInput.MousePosition);
5342 bool isSelected = isMouseOn && GUI.MouseOn ==
null && Widget.SelectedWidgets.None();
5345 case WidgetType.Rectangle:
5348 var rect = drawRect;
5349 rect.Inflate(size * 0.3f, size * 0.3f);
5350 GUI.DrawRectangle(spriteBatch, rect, color, thickness: 3, isFilled: PlayerInput.PrimaryMouseButtonHeld());
5354 GUI.DrawRectangle(spriteBatch, drawRect, color, thickness: 1, isFilled:
false);
5357 case WidgetType.Circle:
5360 ShapeExtensions.DrawCircle(spriteBatch, drawPos, size * 0.7f, 40, color, thickness: 3);
5364 ShapeExtensions.DrawCircle(spriteBatch, drawPos, size * 0.5f, 40, color, thickness: 1);
5367 default:
throw new NotImplementedException(widgetType.ToString());
5372 if (onHovered ==
null)
5374 GUIComponent.DrawToolTip(spriteBatch, toolTip,
new Vector2(drawRect.Right + 5, drawRect.Y - drawRect.Height / 2));
5380 if (PlayerInput.PrimaryMouseButtonHeld())
5382 if (autoFreeze ?? this.autoFreeze)
5386 if (holdPosition ==
true)
5388 character.AnimController.Collider.PhysEnabled =
false;
5394 isFrozen = freezeToggle.Selected;
5395 character.AnimController.Collider.PhysEnabled =
true;
5398 if (PlayerInput.PrimaryMouseButtonClicked())
5406 #region Widgets as classes
5407 private Dictionary<string, Widget> animationWidgets =
new Dictionary<string, Widget>();
5408 private Dictionary<string, Widget> jointSelectionWidgets =
new Dictionary<string, Widget>();
5409 private Dictionary<string, Widget> limbEditWidgets =
new Dictionary<string, Widget>();
5411 private Widget GetAnimationWidget(
string name, Color innerColor, Color? outerColor =
null,
int size = 10,
float sizeMultiplier = 2, WidgetShape shape =
WidgetShape.Rectangle, Action<Widget> initMethod =
null)
5413 string id = $
"{character.SpeciesName}_{character.AnimController.CurrentAnimationParams.AnimationType.ToString()}_{name}";
5414 if (!animationWidgets.TryGetValue(
id, out Widget widget))
5416 int selectedSize = (int)Math.Round(size * sizeMultiplier);
5417 widget =
new Widget(
id, size, shape)
5419 TooltipOffset =
new Vector2(selectedSize / 2 + 5, -10),
5420 Data = character.AnimController.CurrentAnimationParams
5422 widget.MouseUp += () => CurrentAnimation.StoreSnapshot();
5423 widget.Color = innerColor;
5424 widget.SecondaryColor = outerColor;
5425 widget.PreUpdate += dTime =>
5427 widget.Enabled = editAnimations;
5430 AnimationParams data = widget.Data as AnimationParams;
5431 widget.Enabled = data.AnimationType == character.AnimController.CurrentAnimationParams.AnimationType;
5434 widget.PostUpdate += dTime =>
5436 widget.InputAreaMargin = widget.IsControlled ? 1000 : 0;
5437 widget.Size = widget.IsSelected ? selectedSize : size;
5438 widget.IsFilled = widget.IsControlled;
5440 widget.PreDraw += (sp, dTime) =>
5442 if (!widget.IsControlled)
5447 animationWidgets.Add(
id, widget);
5448 initMethod?.Invoke(widget);
5453 private Widget GetJointSelectionWidget(
string id, LimbJoint joint,
string linkedId =
null)
5456 if (!jointSelectionWidgets.TryGetValue(
id, out Widget jointWidget))
5458 jointWidget = CreateJointSelectionWidget(
id, joint);
5459 if (linkedId !=
null)
5461 if (!jointSelectionWidgets.TryGetValue(linkedId, out Widget linkedWidget))
5463 linkedWidget = CreateJointSelectionWidget(linkedId, joint);
5465 jointWidget.LinkedWidget = linkedWidget;
5466 linkedWidget.LinkedWidget = jointWidget;
5472 Widget CreateJointSelectionWidget(
string ID, LimbJoint j)
5474 int normalSize = 10;
5475 int selectedSize = 20;
5476 var widget =
new Widget(ID, normalSize,
WidgetShape.Circle);
5477 widget.Refresh = () =>
5479 widget.ShowTooltip = !selectedJoints.Contains(joint);
5480 widget.Color = selectedJoints.Contains(joint) ? Color.Yellow : GUIStyle.Red;
5483 widget.PreUpdate += dTime => widget.Enabled = editJoints;
5484 widget.PostUpdate += dTime =>
5486 widget.InputAreaMargin = widget.IsControlled ? 1000 : 0;
5487 widget.Size = widget.IsSelected ? selectedSize : normalSize;
5489 widget.MouseDown += () =>
5491 if (jointCreationMode != JointCreationMode.None) {
return; }
5492 if (!selectedJoints.Contains(joint))
5494 if (!Widget.EnableMultiSelect)
5496 selectedJoints.Clear();
5498 selectedJoints.Add(joint);
5500 else if (Widget.EnableMultiSelect)
5502 selectedJoints.Remove(joint);
5504 foreach (var w
in jointSelectionWidgets.Values)
5507 w.LinkedWidget?.Refresh();
5509 ResetParamsEditor();
5511 widget.MouseUp += () =>
5513 if (jointCreationMode == JointCreationMode.None)
5515 RagdollParams.StoreSnapshot();
5518 widget.Tooltip = joint.Params.Name;
5519 widget.TooltipOffset =
new Vector2(-GUIStyle.Font.MeasureString(widget.Tooltip).X - 30, -10);
5520 jointSelectionWidgets.Add(ID, widget);
5525 private Widget GetLimbEditWidget(
string ID, Limb limb,
int size = 5, WidgetShape shape =
WidgetShape.Rectangle, Action < Widget> initMethod =
null)
5527 if (!limbEditWidgets.TryGetValue(ID, out Widget widget))
5529 widget = CreateLimbEditWidget();
5530 limbEditWidgets.Add(ID, widget);
5534 Widget CreateLimbEditWidget()
5536 int normalSize = size;
5537 int selectedSize = (int)Math.Round(size * 1.5f);
5538 var w =
new Widget(ID, size, shape)
5540 TooltipOffset =
new Vector2(selectedSize / 2 + 5, -10),
5542 Color = Color.Yellow,
5543 SecondaryColor = Color.Gray,
5544 TextColor = Color.Yellow
5546 w.PreUpdate += dTime => w.Enabled = editLimbs && selectedLimbs.Contains(limb);
5547 w.PostUpdate += dTime =>
5549 w.InputAreaMargin = w.IsControlled ? 1000 : 0;
5550 w.Size = w.IsSelected ? selectedSize : normalSize;
5551 w.IsFilled = w.IsControlled;
5553 w.MouseUp += () => RagdollParams.StoreSnapshot();
5554 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
void GiveJobItems(WayPoint spawnPoint=null)
readonly AnimController AnimController
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)
static void DrawFront(SpriteBatch spriteBatch, bool editing=false, Predicate< MapEntity > predicate=null)
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)