Forgot all about vitruvian bones. 
Code: Select all
-- **************************************************
-- Provide Moho with the name of this script object
-- **************************************************
ScriptName = "LM_ReparentBone"
-- **************************************************
-- General information about this script
-- **************************************************
LM_ReparentBone = {}
LM_ReparentBone.BASE_STR = 2230
function LM_ReparentBone:Name()
	return "Reparent Bone"
end
function LM_ReparentBone:Version()
	return "6.0"
end
function LM_ReparentBone:Description()
	return MOHO.Localize("/Scripts/Tool/ReparentBone/Description=Click to attach bone to a new parent (hold <alt> to select a new bone, <ctrl/cmd> to select a target bone)")
end
function LM_ReparentBone:Creator()
	return "Lost Marble LLC"
end
function LM_ReparentBone:UILabel()
	return(MOHO.Localize("/Scripts/Tool/ReparentBone/ReparentBone=Reparent Bone"))
end
-- **************************************************
-- The guts of this script
-- **************************************************
function LM_ReparentBone:IsEnabled(moho)
	if (moho:CountBones() < 2) then
		return false
	end
	if (moho.frame > 0 and not MOHO.IsMohoPro()) then
		return false
	end
	return true
end
function LM_ReparentBone:IsRelevant(moho)
	local skel = moho:Skeleton()
	if (skel == nil) then
		return false
	end
	if (moho.frame > 0 and not MOHO.IsMohoPro()) then
		return false
	end
	return true
end
function LM_ReparentBone:OnMouseDown(moho, mouseEvent)
	moho.document:PrepUndo(moho.layer, true)
	moho.document:SetDirty()
	self:OnReparent(moho, mouseEvent)
end
function LM_ReparentBone:OnMouseMoved(moho, mouseEvent)
	self:OnReparent(moho, mouseEvent)
end
function LM_ReparentBone:OnMouseUp(moho, mouseEvent)
	moho:UpdateUI()
end
function LM_ReparentBone:OnKeyDown(moho, keyEvent)
	LM_SelectBone:OnKeyDown(moho, keyEvent)
end
function LM_ReparentBone:DrawMe(moho, view)
	local skel = moho:Skeleton()
	if (skel == nil) then
		return
	end
	local g = view:Graphics()
	local matrix = LM.Matrix:new_local()
	local bonePt = LM.Vector2:new_local()
	local parentPt = LM.Vector2:new_local()
	local arrowPt1 = LM.Vector2:new_local()
	local arrowPt2 = LM.Vector2:new_local()
	moho.layer:GetFullTransform(moho.frame, matrix, moho.document)
	g:Push()
	g:ApplyMatrix(matrix)
	g:SetSmoothing(true)
	for i = 0, skel:CountBones() - 1 do
		local bone = skel:Bone(i)
--[syn]		if (bone.fParent >= 0) then
--[[syn]]		if (bone.fParent >= 0) and (not bone.fHidden --[[and not bone.fShy]]) and (bone:IsGroupVisible()) then
			local parentBone = skel:Bone(bone.fParent)
			bonePt:Set(bone.fLength / 2, 0)
			bone.fMovedMatrix:Transform(bonePt)
			parentPt:Set(parentBone.fLength / 2, 0)
			parentBone.fMovedMatrix:Transform(parentPt)
			local v = parentPt - bonePt
			local mag = v:Mag()
			local f = mag
			if (f > 0.2) then
				f = 0.2
			end
			arrowPt1:Set(mag - f / 4, -f / 16)
			arrowPt2:Set(mag - f / 4, f / 16)
			f = math.atan2(v.y, v.x)
			arrowPt1:Rotate(f)
			arrowPt2:Rotate(f)
			arrowPt1.x = arrowPt1.x + bonePt.x
			arrowPt1.y = arrowPt1.y + bonePt.y
			arrowPt2.x = arrowPt2.x + bonePt.x
			arrowPt2.y = arrowPt2.y + bonePt.y
			if (bone.fSelected) then
				g:SetColor(MOHO.MohoGlobals.SelCol)
			else
				g:SetColor(MOHO.MohoGlobals.ElemCol)
			end
			g:DrawLine(bonePt.x, bonePt.y, parentPt.x, parentPt.y)
			g:BeginShape()
			g:AddLine(parentPt, arrowPt1)
			g:AddLine(arrowPt1, arrowPt2)
			g:AddLine(arrowPt2, parentPt)
			g:EndShape()
		end
	end
	g:Pop()
end
function LM_ReparentBone:OnReparent(moho, mouseEvent)
	if (mouseEvent.altKey) then
		LM_SelectBone:Select(moho, mouseEvent.pt, mouseEvent.vec, mouseEvent.view, mouseEvent.shiftKey, mouseEvent.ctrlKey)
		return
	end
	local skel = moho:Skeleton()
	if (skel == nil) then
		return
	end
	if (moho:CountSelectedBones() < 1) then
		return
	end
	-- let the user pick another bone - this will be the new parent
	local parentID = mouseEvent.view:PickBone(mouseEvent.pt, mouseEvent.vec, moho.layer, true)
	local parentFrame = moho.layerFrame -- 0
	if (mouseEvent.ctrlKey) then
		local targetChanged = false
		for i = 0, skel:CountBones() - 1 do
			local bone = skel:Bone(i)
			if (bone.fSelected and parentID ~= bone.fTargetBone:GetValue(moho.layerFrame)) then
				bone.fTargetBone:SetValue(moho.layerFrame, parentID)
				targetChanged = true
			end
		end
		if (targetChanged) then
			moho:NewKeyframe(CHANNEL_BONE_TARGET)
		end
	else
		for i = 0, skel:CountBones() - 1 do
			local bone = skel:Bone(i)
			if (bone.fSelected) then
				if (not skel:IsBoneChild(i, parentID)) then
					-- new parent chosen
					local v1 = LM.Vector2:new_local()
					local v2 = LM.Vector2:new_local()
					v1:Set(0, 0)
					if (bone:IsZeroLength()) then
						v2:Set(0.1, 0)
					else
						v2:Set(bone.fLength, 0)
					end
					bone.fMovedMatrix:Transform(v1)
					bone.fMovedMatrix:Transform(v2)
					if (parentID >= 0) then
						local invMatrix = LM.Matrix:new_local()
						local parent = skel:Bone(parentID)
						invMatrix:Set(parent.fMovedMatrix)
						invMatrix:Invert()
						invMatrix:Transform(v1)
						invMatrix:Transform(v2)
					end
					if (parentFrame ~= 0) then
						bone.fAnimPos:AddKey(parentFrame - 1)
						if (bone:ParentalFlipFactor() < 0.0) then
							bone.fFlipV:SetValue(parentFrame, not bone.fFlipV:GetValue(parentFrame))
						end
					end
					bone.fAnimPos:SetValue(parentFrame, v1)
					v2 = v2 - v1
					local angle = math.atan2(v2.y, v2.x)
					while angle > 2 * math.pi do
						angle = angle - 2 * math.pi
					end
					while angle < 0 do
						angle = angle + 2 * math.pi
					end
					if (bone.fFixedAngle) then
						if (parentFrame == 0) then
							bone.fAnimAngle:SetValue(parentFrame, angle)
						end
					else
						local angleDiff = angle - bone.fAnimAngle:GetValue(parentFrame)
						if (parentFrame ~= 0) then
							bone.fAnimAngle:AddKey(parentFrame - 1)
						end
						bone.fAnimAngle:SetValue(parentFrame, angle)
						-- update future angle keyframes for relative offsets just like this frame
						for keyID = 0, bone.fAnimAngle:CountKeys() - 1 do
							local angleFrame = bone.fAnimAngle:GetKeyWhen(keyID)
							if (angleFrame > parentFrame) then
								local newAngle = bone.fAnimAngle:GetValue(angleFrame) + angleDiff
								bone.fAnimAngle:SetValue(angleFrame, newAngle)
							end
						end
						if (parentFrame == 0) then -- update action keyframes for relative offsets just like this frame
							for actionID = 0, bone.fAnimAngle:CountActions() - 1 do
								local action = moho:ChannelAsAnimVal(bone.fAnimAngle:Action(actionID))
								for keyID = 0, action:CountKeys() - 1 do
									local angleFrame = action:GetKeyWhen(keyID)
									if (angleFrame > parentFrame) then
										local newAngle = action:GetValue(angleFrame) + angleDiff
										action:SetValue(angleFrame, newAngle)
									end
								end
							end
						end
					end
					if (bone.fAnimParent:CountKeys() < 2) then
						bone.fAnimParent:SetValue(0, bone.fParent)
					end
					bone.fParent = parentID
					bone.fAnimParent:SetValue(parentFrame, parentID)
					if (parentFrame ~= 0) then
						if (bone:ParentalFlipFactor() < 0.0) then
							bone.fFlipV:SetValue(parentFrame, not bone.fFlipV:GetValue(parentFrame))
						end
					end
				end
			end
		end
		moho:NewKeyframe(CHANNEL_BONE)
		moho:NewKeyframe(CHANNEL_BONE_T)
		moho:NewKeyframe(CHANNEL_BONE_PARENT)
	end
	moho.layer:UpdateCurFrame()
	mouseEvent.view:DrawMe()
	moho:UpdateSelectedChannels()
end
-- **************************************************
-- Tool options - create and respond to tool's UI
-- **************************************************
LM_ReparentBone.CHANGE = MOHO.MSG_BASE
LM_ReparentBone.DUMMY = MOHO.MSG_BASE + 1
LM_ReparentBone.SELECTITEM = MOHO.MSG_BASE + 2
function LM_ReparentBone:DoLayout(moho, layout)
	self.menu = LM.GUI.Menu(MOHO.Localize("/Scripts/Tool/ReparentBone/SelectBone=Select Bone"))
	self.popup = LM.GUI.PopupMenu(128, false)
	self.popup:SetMenu(self.menu)
	layout:AddChild(self.popup)
end
function LM_ReparentBone:UpdateWidgets(moho)
	local skel = moho:Skeleton()
	if (skel == nil) then
		return
	end
	MOHO.BuildBoneMenu(self.menu, skel, self.SELECTITEM, self.DUMMY)
end
function LM_ReparentBone:HandleMessage(moho, view, msg)
	local skel = moho:Skeleton()
	if (skel == nil) then
		return
	end
	if (msg >= self.SELECTITEM) then
		for i = 0, skel:CountBones() - 1 do
			skel:Bone(i).fSelected = (i == msg - self.SELECTITEM)
		end
		moho:UpdateUI()
	end
end