Transformation System with Shadow Form Template + Free Crawling Animations

I wrote this algorithm today to manage transformations.
The transformation included in the model is called “Shadow Form”. It is triggered when you have 1 health and you cannot equip tools while you are in this form and you crawl.
I designed this as a system to allow support for other transformations and easy code refactoring into a module.

This is the source code of the transformation system.

local Character=script.Parent
local formname="Form"
local Forms={}
local function Form(Character,FormName,bool)
	local FormObject=Character:FindFirstChild(FormName) or Instance.new("BoolValue")
	if FormObject.Name~=FormName then
		FormObject.Name=FormName
		FormObject.Parent=Character
	end
	if bool then
	FormObject.Value=bool
	end
	return FormObject
end
local FormData={}
local animcache=script.AnimationCache
local function ReplaceAnimations(library,AnimationScript)
	local payload={}
	local objects={}
	for i,v in library:GetChildren() do
		local cachethis= AnimationScript:FindFirstChild(v.Name)
		if cachethis then		
			table.insert(payload,cachethis)
			cachethis.Parent=animcache
			v.Parent=AnimationScript
			table.insert(objects,v)
		end
	end
	return {payload=payload,objects=objects}
end
local faces={"Front","Left","Right","Bottom","Top","Back"}
local ts=game:GetService("TweenService")
local debris=game:GetService("Debris")
local imagetrans=TweenInfo.new(5)

local function covertexture(imageobject,subject)
	local images={}
	for i,v in faces do
		local imageclone=imageobject:Clone()
		imageclone.Face=v
		imageclone.Transparency=1
		imageclone.Parent=subject
		table.insert(images,imageclone)
		ts:Create(imageclone,imagetrans,{Transparency=0.3}):Play()
	end
	return images
end

local function playsound(sound)
	sound.Volume=0
	sound.Pitch=math.random(75,125)/100
	sound.PlaybackSpeed=math.random(75,125)/100
	--sound.Parent=Character.PrimaryPart
	sound:Play()
	ts:Create(sound,imagetrans,{Volume=math.random(30,100)/100}):Play()
	
end
FormData={
	["Dark Form"]={
		[true]=function(Character)
			local payload={}
			local colorhash={}
			local texturepayload={}
			local particlepayload={}
			
			for i,v in Character:GetChildren() do 
				if v:IsA("BasePart") and v.Transparency~=1 then
					table.insert(payload,{object=v,Color=v.Color})
					ts:Create(v,imagetrans,{Color=Color3.fromRGB(0,0,0)}):Play()
					colorhash[v.Name]=true	
					for t,o in script["Dark Form Particles"]:GetChildren() do 
						local c=o:Clone()
						c.Parent=v
						c.Enabled=true
						table.insert(particlepayload,c)
					end
				end
			end
			local sound=script["Dark Form Sounds"]["Hollow Rumble 1 (SFX)"]:Clone()
			sound.Parent=Character.PrimaryPart
			playsound(sound)
			for i,v in Character:GetDescendants() do
				if v:IsA("BasePart") and v.Transparency~=1 and colorhash[v.Name]==nil then
					table.insert(texturepayload,covertexture(script.Black,v))
				end
			end 
			local animpayload=ReplaceAnimations(script["Dark Form"],Character.Animate)
			FormData["Dark Form"].Data["Colors"]=payload
			FormData["Dark Form"].Data["Animations"]=animpayload
			FormData["Dark Form"].Data["Textures"]=texturepayload
			FormData["Dark Form"].Data["Particles"]=particlepayload
			FormData["Dark Form"].Data["Sounds"]={sound}
			end,
		[false]=function(Character)
			if FormData["Dark Form"].Data.Colors and #FormData["Dark Form"].Data.Colors>1 then
			for i,v in FormData["Dark Form"].Data.Colors do 
				ts:Create(v.object,imagetrans,{Color=v.Color}):Play()
			end	
			for i,v in FormData["Dark Form"].Data.Textures do 
				for t,o in v do 
				ts:Create(o,imagetrans,{Transparency=1}):Play()
				debris:AddItem(o,7)
				end
			end
			FormData["Dark Form"].Data.Textures={}
			for i,v in FormData["Dark Form"].Data.Sounds do 
					ts:Create(v,imagetrans,{Volume=0}):Play()
					debris:AddItem(v,8)
			end	
			FormData["Dark Form"].Data.Sounds={}
			for i,v in FormData["Dark Form"].Data.Particles do 
				v.Enabled=false 
				debris:AddItem(v,5)
				--v:Destroy()
			end
			
			FormData["Dark Form"].Data.Particles={}
			FormData["Dark Form"].Data.Colors={}
			local v =FormData["Dark Form"].Data.Animations-- do 
				for t,o in v.payload do 
					o.Parent=Character.Animate
				end
				for t,o in v.objects do 
					o.Parent=script["Dark Form"]
				end
			--end	
			FormData["Dark Form"].Data.Animations={}
		end 
		end,
		["Data"]={},
		Connections=function(Character)
			local a=Character.ChildAdded:Connect(function(v)
				if v:IsA("Tool") then
					Character.Humanoid:UnequipTools()
				end
			end)
			local b=Character.Humanoid:GetPropertyChangedSignal("Health"):Connect(function()
			if Character.Humanoid.Health<=1 then
				Character.Humanoid.Health=1				
				Forms.Toggle(Character,"Dark Form",true)
			elseif Character.Humanoid.Health>=Character.Humanoid.MaxHealth*.25 then
				Forms.Toggle(Character,"Dark Form",false)
			end
			end)
			return {a,b}
		end,
	},	
}

function Forms.Toggle(Character,FormName,bool)
	local formobject=Form(Character,FormName)
	if bool==nil then
	if formobject.Value==false then
		bool=true
		formobject.Value=true
	else bool=false 	
	end
	--FormData[FormName][bool](Character)
	elseif formobject==bool then
		return 
	end
		FormData[FormName][bool](Character)
	
end
function Forms.Initalize(Character,FormHash)
local connections={}
for i,v in FormData do
	if FormHash==nil or FormHash[i]~=nil then
	Form(Character,formname)
	table.insert(connections,v.Connections(Character))
	end
end
local function disconnect()
	for i,group in connections do
		for t,connection in group do 
			connection:Disconnect()
		end
	end
end
table.insert(connections,Character.AncestryChanged:Connect(function()
	if Character.Parent~=workspace then
		disconnect()
	end
end))

end


image
image
image

This is the model you can try out by placing it in StarterCharacterScripts
PlaceScriptIn_StarterCharacterScripts - Creator Store (roblox.com)

If you want to use the animations you can import them with my free plugin bulk animation importer.
Bulk Animation Importer - Creator Store (roblox.com)

It is based off Sora’s Shadow form design from Kingdom Hearts II

6 Likes

I published an update to the code fixing a bug with the form toggle function This is the changes

function Forms.Toggle(Character,FormName,bool)
	local formobject=Form(Character,FormName)
	if bool==nil then
	if formobject.Value==false then
		bool=true
		formobject.Value=true
	else 
		formobject.Value=false
		bool=false 	
	end
	elseif formobject.Value==bool then
		return 			
	end
	formobject.Value=bool
	FormData[FormName][bool](Character)
	
end

This function ensures that a trigger is not called twice while it is on.