Fix again

Fix again

--
-- Copyright (C) 2017-2018 DBot
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--

import FrameNumberL, RealTimeL, StrongEntity, PPM2 from _G
import ALTERNATIVE_RENDER from PPM2
import GetPonyData, IsDormant, PPMBonesModifier, IsPony from FindMetaTable('Entity')

RENDER_HORN_GLOW = CreateConVar('ppm2_horn_glow', '1', {FCVAR_ARCHIVE, FCVAR_NOTIFY}, 'Visual horn glow when player uses physgun')
HORN_PARTICLES = CreateConVar('ppm2_horn_particles', '1', {FCVAR_ARCHIVE, FCVAR_NOTIFY}, 'Visual horn particles when player uses physgun')
HORN_FP = CreateConVar('ppm2_horn_firstperson', '1', {FCVAR_ARCHIVE, FCVAR_NOTIFY}, 'Visual horn effetcs in first person')
HORN_HIDE_BEAM = CreateConVar('ppm2_horn_nobeam', '1', {FCVAR_ARCHIVE, FCVAR_NOTIFY}, 'Hide physgun beam')
TASK_RENDER_TYPE = CreateConVar('ppm2_task_render_type', '1', {FCVAR_ARCHIVE, FCVAR_NOTIFY}, 'Task rendering type (e.g. pony ragdolls and NPCs). 1 - better render; less conflicts; more FPS. 0 - "old-style" render; possible conflicts;')
DRAW_LEGS_DEPTH = CreateConVar('ppm2_render_legsdepth', '1', {FCVAR_ARCHIVE, FCVAR_NOTIFY}, 'Render legs in depth pass. Useful with Boken DoF enabled')
LEGS_RENDER_TYPE = CreateConVar('ppm2_render_legstype', '0', {FCVAR_ARCHIVE, FCVAR_NOTIFY}, 'When render legs. 0 - Before Opaque renderables; 1 - after Translucent renderables')
ENABLE_NEW_RAGDOLLS = CreateConVar('ppm2_sv_new_ragdolls', '1', {FCVAR_NOTIFY, FCVAR_REPLICATED}, 'Enable new ragdolls')
SHOULD_DRAW_VIEWMODEL = CreateConVar('ppm2_cl_draw_hands', '1', {FCVAR_ARCHIVE}, 'Should draw hooves as viewmodel')
SV_SHOULD_DRAW_VIEWMODEL = CreateConVar('ppm2_sv_draw_hands', '1', {FCVAR_NOTIFY, FCVAR_REPLICATED}, 'Should draw hooves as viewmodel')

hook.Add 'PreDrawPlayerHands', 'PPM2.ViewModel', (arms = NULL, viewmodel = NULL, ply = LocalPlayer(), weapon = NULL) ->
	return if PPM2.__RENDERING_REFLECTIONS
	return true unless SV_SHOULD_DRAW_VIEWMODEL\GetBool()
	return true unless SHOULD_DRAW_VIEWMODEL\GetBool()
	return unless IsValid(arms)
	return unless ply.__cachedIsPony
	return unless ply\Alive()
	arms\SetPos(LocalPlayer()\EyePos() + Vector(0, 0, 100))
	wep = ply\GetActiveWeapon()
	if IsValid(wep) and wep.UseHands == false
		return true -- Dafuck?
	return if arms\GetModel() ~= 'models/cppm/pony_arms.mdl'
	data = ply\GetPonyData()
	return unless data
	status = data\GetRenderController()\PreDrawArms(arms)
	return status if status ~= nil
	arms.__ppm2_draw = true

hook.Add 'PostDrawPlayerHands', 'PPM2.ViewModel', (arms = NULL, viewmodel = NULL, ply = LocalPlayer(), weapon = NULL) ->
	return if PPM2.__RENDERING_REFLECTIONS
	return unless IsValid(arms)
	return unless arms.__ppm2_draw
	data = ply\GetPonyData()
	return unless data
	data\GetRenderController()\PostDrawArms(arms)
	arms.__ppm2_draw = false

IN_DRAW = false
MARKED_FOR_DRAW = {}

PPM2.PreDrawOpaqueRenderables = (bDrawingDepth, bDrawingSkybox) ->
	return if IN_DRAW or PPM2.__RENDERING_REFLECTIONS

	MARKED_FOR_DRAW = {}

	if not ALTERNATIVE_RENDER\GetBool()
		for ply in *player.GetAll()
			if not IsDormant(ply)
				p = IsPony(ply)
				ply.__cachedIsPony = p
				if p
					data = GetPonyData(ply)
					if data
						renderController = data\GetRenderController()
						if renderController
							renderController\PreDraw()
							table.insert(MARKED_FOR_DRAW, renderController)

	if bDrawingDepth and DRAW_LEGS_DEPTH\GetBool()
		with LocalPlayer()
			if .__cachedIsPony and \Alive()
				if data = \GetPonyData()
					IN_DRAW = true
					data\GetRenderController()\DrawLegsDepth()
					IN_DRAW = false

	return if bDrawingDepth or bDrawingSkybox

	if not LEGS_RENDER_TYPE\GetBool()
		with LocalPlayer()
			if .__cachedIsPony and \Alive()
				if data = \GetPonyData()
					IN_DRAW = true
					data\GetRenderController()\DrawLegs()
					IN_DRAW = false

PPM2.PostDrawTranslucentRenderables = (bDrawingDepth, bDrawingSkybox) ->
	if not ALTERNATIVE_RENDER\GetBool() and not bDrawingDepth and not bDrawingSkybox
		for draw in *MARKED_FOR_DRAW
			draw\PostDraw()

PPM2.PostDrawOpaqueRenderables = (bDrawingDepth, bDrawingSkybox) ->
	return if IN_DRAW or PPM2.__RENDERING_REFLECTIONS

	if bDrawingDepth and DRAW_LEGS_DEPTH\GetBool()
		with LocalPlayer()
			if .__cachedIsPony and \Alive()
				if data = \GetPonyData()
					IN_DRAW = true
					data\GetRenderController()\DrawLegsDepth()
					IN_DRAW = false

	return if bDrawingDepth or bDrawingSkybox

	if not TASK_RENDER_TYPE\GetBool()
		for task in *PPM2.NetworkedPonyData.RenderTasks
			ent = task.ent
			if IsValid(ent)
				if ent.__cachedIsPony
					ent\SetNoDraw(true)
					ent.__ppm2_task_hit = true
					renderController = task\GetRenderController()
					renderController\PreDraw(ent)
					IN_DRAW = true
					ent\DrawModel()
					IN_DRAW = false
					renderController\PostDraw(ent)
				else
					if ent.__ppm2_task_hit
						ent.__ppm2_task_hit = false
						ent\SetNoDraw(false)
						task\Reset()

	if not ENABLE_NEW_RAGDOLLS\GetBool()
		for ply in *player.GetAll()
			alive = ply\Alive()
			ply.__ppm2_last_dead = RealTimeL() + 2 if not alive
			if ply.__cachedIsPony
				if ply\GetPonyData() and not alive
					data = ply\GetPonyData()
					rag = ply\GetRagdollEntity()
					if IsValid(rag)
						renderController = data\GetRenderController()
						data\DoRagdollMerge()
						if renderController
							renderController\PreDraw(rag)
							IN_DRAW = true
							rag\DrawModel()
							IN_DRAW = false
							renderController\PostDraw(rag)

	if LEGS_RENDER_TYPE\GetBool()
		with LocalPlayer()
			if .__cachedIsPony and \Alive()
				if data = \GetPonyData()
					IN_DRAW = true
					data\GetRenderController()\DrawLegs()
					IN_DRAW = false

Think = ->
	if TASK_RENDER_TYPE\GetBool()
		for task in *PPM2.NetworkedPonyData.RenderTasks
			ent = task.ent
			if IsValid(ent) and ent.__cachedIsPony
				if ent.__ppm2_task_hit
					ent.__ppm2_task_hit = false
					ent\SetNoDraw(false)

				if not ent.__ppm2RenderOverride
					ent = ent\GetEntity()
					ent.__ppm2_oldRenderOverride = ent.RenderOverride
					ent.__ppm2RenderOverride = ->
						renderController = task\GetRenderController()
						renderController\PreDraw(ent, true)
						ent\DrawModel()
						renderController\PostDraw(ent, true)
						ent.__ppm2_oldRenderOverride(ent) if ent.__ppm2_oldRenderOverride
					ent.RenderOverride = ent.__ppm2RenderOverride

PPM2.PrePlayerDraw = =>
	return if PPM2.__RENDERING_REFLECTIONS
	if ALTERNATIVE_RENDER\GetBool()
		with data = GetPonyData(@)
			return if not data
			@__cachedIsPony = IsPony(@)
			return if not @__cachedIsPony
			f = FrameNumberL()
			return if @__ppm2_last_draw == f
			@__ppm2_last_draw = f
			@__ppm2_last_dead = @__ppm2_last_dead or 0
			return if @__ppm2_last_dead > RealTimeL()
			bones = PPMBonesModifier(@)
			if data and bones\CanThink()
				@ResetBoneManipCache()
				bones\ResetBones()
				hook.Call('PPM2.SetupBones', nil, StrongEntity(@), data) if data
				bones\Think()
				@ApplyBoneManipulations()
				@_ppmBonesModified = true
			renderController = data\GetRenderController()
			status = renderController\PreDraw() if renderController
	else
		with data = GetPonyData(@)
			return if not data
			@__cachedIsPony = IsPony(@)
			return if not @__cachedIsPony
			f = FrameNumberL()
			return if @__ppm2_last_draw == f
			@__ppm2_last_draw = f
			bones = PPMBonesModifier(@)
			if data and bones\CanThink()
				@ResetBoneManipCache()
				bones\ResetBones()
				hook.Call('PPM2.SetupBones', nil, StrongEntity(@), data) if data
				bones\Think()
				@ApplyBoneManipulations()
				@_ppmBonesModified = true

PPM2.PostPlayerDraw = =>
	return if not ALTERNATIVE_RENDER\GetBool() or PPM2.__RENDERING_REFLECTIONS
	with data = GetPonyData(@)
		return if not data or not @__cachedIsPony
		renderController = data\GetRenderController()
		renderController\PostDraw() if renderController

do
	hornGlowStatus = {}
	smokeMaterial = 'ppm2/hornsmoke'
	fireMat = 'particle/fire'
	hornShift = Vector(1, 0.15, 14.5)

	hook.Add 'PreDrawHalos', 'PPM2.HornEffects', =>
		return if not HORN_HIDE_BEAM\GetBool()
		frame = FrameNumberL()
		cTime = (RealTimeL() % 20) * 4
		for ent, status in pairs hornGlowStatus
			if IsValid(ent) and status.frame == frame and IsValid(status.target)
				additional = math.sin(cTime / 2 + status.haloSeed * 3) * 40
				newCol = DLib.AddColor(status.color, Color(additional, additional, additional))
				halo.Add({status.target}, newCol, math.sin(cTime + status.haloSeed) * 4 + 8, math.cos(cTime + status.haloSeed) * 4 + 8, 2)

	hook.Add 'Think', 'PPM2.HornEffects', =>
		frame = FrameNumberL()
		for ent, status in pairs hornGlowStatus
			if not IsValid(ent)
				status.emmiter\Finish() if IsValid(status.emmiter)
				status.emmiterProp\Finish() if IsValid(status.emmiterProp)
				hornGlowStatus[ent] = nil
			elseif status.frame ~= frame
				status.data\SetHornGlow(status.prevStatus)
				status.emmiter\Finish() if IsValid(status.emmiter)
				status.emmiterProp\Finish() if IsValid(status.emmiterProp)
				hornGlowStatus[ent] = nil
			else
				if not status.prevStatus and RENDER_HORN_GLOW\GetBool() and status.data\GetHornGlow() ~= status.isEnabled
					status.data\SetHornGlow(status.isEnabled)
				if status.attach and IsValid(status.target)
					grabHornPos = Vector(hornShift) * status.data\GetPonySize()
					{:Pos, :Ang} = ent\GetAttachment(status.attach)
					grabHornPos\Rotate(Ang)
					if status.isEnabled and IsValid(status.emmiter) and status.nextSmokeParticle < RealTimeL()
						status.nextSmokeParticle = RealTimeL() + math.Rand(0.1, 0.2)
						for i = 1, math.random(1, 4)
							vec = VectorRand()
							calcPos = Pos + grabHornPos + vec
							with particle = status.emmiter\Add(smokeMaterial, calcPos)
								\SetRollDelta(math.rad(math.random(0, 360)))
								\SetPos(calcPos)
								life = math.Rand(0.6, 0.9)
								\SetStartAlpha(math.random(80, 170))
								\SetDieTime(life)
								\SetColor(status.color.r, status.color.g, status.color.b)
								\SetEndAlpha(0)
								size = math.Rand(2, 3)
								\SetEndSize(math.Rand(2, size))
								\SetStartSize(size)
								\SetGravity(Vector())
								\SetAirResistance(10)
								vecRand = VectorRand()
								vecRand.z *= 2
								\SetVelocity(ent\GetVelocity() + vecRand * status.data\GetPonySize() * 2)
								\SetCollide(false)
					if status.isEnabled and IsValid(status.emmiterProp) and status.nextGrabParticle < RealTimeL() and status.mins and status.maxs
						status.nextGrabParticle = RealTimeL() + math.Rand(0.05, 0.3)
						status.emmiterProp\SetPos(status.tpos)
						for i = 1, math.random(2, 6)
							calcPos = Vector(math.Rand(status.mins.x, status.maxs.x), math.Rand(status.mins.y, status.maxs.y), math.Rand(status.mins.z, status.maxs.z))
							with particle = status.emmiterProp\Add(fireMat, calcPos)
								\SetRollDelta(math.rad(math.random(0, 360)))
								\SetPos(calcPos)
								life = math.Rand(0.5, 0.9)
								\SetStartAlpha(math.random(130, 230))
								\SetDieTime(life)
								\SetColor(status.color.r, status.color.g, status.color.b)
								\SetEndAlpha(0)
								\SetEndSize(0)
								\SetStartSize(math.Rand(2, 6))
								\SetGravity(Vector())
								\SetAirResistance(15)
								\SetVelocity(VectorRand() * 6)
								\SetCollide(false)

	hook.Add 'DrawPhysgunBeam', 'PPM2.HornEffects', (physgun = NULL, isEnabled = false, target = NULL, bone = 0, hitPos = Vector()) =>
		return if not @IsPony() or not HORN_FP\GetBool() and @ == LocalPlayer() and not @ShouldDrawLocalPlayer()
		data = @GetPonyData()
		return if not data
		return if data\GetRace() ~= PPM2.RACE_UNICORN and data\GetRace() ~= PPM2.RACE_ALICORN
		if not hornGlowStatus[@]
			hornGlowStatus[@] = {
				frame: FrameNumberL()
				prevStatus: data\GetHornGlow()
				:data, :isEnabled, :hitPos, :target, :bone
				tpos: @GetPos()
				attach: @LookupAttachment('eyes')
				nextSmokeParticle: 0
				nextGrabParticle: 0
			}

			with hornGlowStatus[@]
				if HORN_PARTICLES\GetBool()
					.emmiter = ParticleEmitter(EyePos())
					.emmiterProp = ParticleEmitter(EyePos())

				.color = data\GetHornMagicColor()
				.haloSeed = math.rad(math.random(-1000, 1000))

				if not data\GetSeparateMagicColor()
					if not data\GetSeparateEyes()
						.color = DLib.LerpColor(0.5, data\GetEyeIrisTop(), data\GetEyeIrisBottom())
					else
						lerpLeft = DLib.LerpColor(0.5, data\GetEyeIrisTopLeft(), data\GetEyeIrisBottomLeft())
						lerpRight = DLib.LerpColor(0.5, data\GetEyeIrisTopRight(), data\GetEyeIrisBottomRight())
						.color = DLib.LerpColor(0.5, lerpLeft, lerpRight)
		else
			with hornGlowStatus[@]
				.frame = FrameNumberL()
				.isEnabled = isEnabled
				.target = target
				.bone = bone
				.hitPos = hitPos
				if IsValid(target)
					.tpos = target\GetPos() + hitPos
					center = target\WorldSpaceCenter()
					.center = center
					mins, maxs = target\WorldSpaceAABB()
					.mins = center + (mins - center) * 1.2
					.maxs = center + (maxs - center) * 1.2
		return false if HORN_HIDE_BEAM\GetBool() and IsValid(target)

hook.Add 'PrePlayerDraw', 'PPM2.PlayerDraw', PPM2.PrePlayerDraw, -2
hook.Add 'PostPlayerDraw', 'PPM2.PostPlayerDraw', PPM2.PostPlayerDraw, -2
hook.Add 'PostDrawOpaqueRenderables', 'PPM2.PostDrawOpaqueRenderables', PPM2.PostDrawOpaqueRenderables, -2
hook.Add 'Think', 'PPM2.UpdateRenderTasks', Think, -2
hook.Add 'PreDrawOpaqueRenderables', 'PPM2.PreDrawOpaqueRenderables', PPM2.PreDrawOpaqueRenderables, -2
hook.Add 'PostDrawTranslucentRenderables', 'PPM2.PostDrawTranslucentRenderables', PPM2.PostDrawTranslucentRenderables, -2

--
-- Copyright (C) 2017-2018 DBot
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--

import FrameNumberL, RealTimeL, StrongEntity, PPM2 from _G
import ALTERNATIVE_RENDER from PPM2
import GetPonyData, IsDormant, PPMBonesModifier, IsPony from FindMetaTable('Entity')

RENDER_HORN_GLOW = CreateConVar('ppm2_horn_glow', '1', {FCVAR_ARCHIVE, FCVAR_NOTIFY}, 'Visual horn glow when player uses physgun')
HORN_PARTICLES = CreateConVar('ppm2_horn_particles', '1', {FCVAR_ARCHIVE, FCVAR_NOTIFY}, 'Visual horn particles when player uses physgun')
HORN_FP = CreateConVar('ppm2_horn_firstperson', '1', {FCVAR_ARCHIVE, FCVAR_NOTIFY}, 'Visual horn effetcs in first person')
HORN_HIDE_BEAM = CreateConVar('ppm2_horn_nobeam', '1', {FCVAR_ARCHIVE, FCVAR_NOTIFY}, 'Hide physgun beam')
TASK_RENDER_TYPE = CreateConVar('ppm2_task_render_type', '1', {FCVAR_ARCHIVE, FCVAR_NOTIFY}, 'Task rendering type (e.g. pony ragdolls and NPCs). 1 - better render; less conflicts; more FPS. 0 - "old-style" render; possible conflicts;')
DRAW_LEGS_DEPTH = CreateConVar('ppm2_render_legsdepth', '1', {FCVAR_ARCHIVE, FCVAR_NOTIFY}, 'Render legs in depth pass. Useful with Boken DoF enabled')
LEGS_RENDER_TYPE = CreateConVar('ppm2_render_legstype', '0', {FCVAR_ARCHIVE, FCVAR_NOTIFY}, 'When render legs. 0 - Before Opaque renderables; 1 - after Translucent renderables')
ENABLE_NEW_RAGDOLLS = CreateConVar('ppm2_sv_new_ragdolls', '1', {FCVAR_NOTIFY, FCVAR_REPLICATED}, 'Enable new ragdolls')
SHOULD_DRAW_VIEWMODEL = CreateConVar('ppm2_cl_draw_hands', '1', {FCVAR_ARCHIVE}, 'Should draw hooves as viewmodel')
SV_SHOULD_DRAW_VIEWMODEL = CreateConVar('ppm2_sv_draw_hands', '1', {FCVAR_NOTIFY, FCVAR_REPLICATED}, 'Should draw hooves as viewmodel')

hook.Add 'PreDrawPlayerHands', 'PPM2.ViewModel', (arms = NULL, viewmodel = NULL, ply = LocalPlayer(), weapon = NULL) ->
	return if PPM2.__RENDERING_REFLECTIONS
	return true unless SV_SHOULD_DRAW_VIEWMODEL\GetBool()
	return true unless SHOULD_DRAW_VIEWMODEL\GetBool()
	return unless IsValid(arms)
	return unless ply.__cachedIsPony
	return unless ply\Alive()
	arms\SetPos(LocalPlayer()\EyePos() + Vector(0, 0, 100))
	wep = ply\GetActiveWeapon()
	if IsValid(wep) and wep.UseHands == false
		return true -- Dafuck?
	return if arms\GetModel() ~= 'models/cppm/pony_arms.mdl'
	data = ply\GetPonyData()
	return unless data
	status = data\GetRenderController()\PreDrawArms(arms)
	return status if status ~= nil
	arms.__ppm2_draw = true

hook.Add 'PostDrawPlayerHands', 'PPM2.ViewModel', (arms = NULL, viewmodel = NULL, ply = LocalPlayer(), weapon = NULL) ->
	return if PPM2.__RENDERING_REFLECTIONS
	return unless IsValid(arms)
	return unless arms.__ppm2_draw
	data = ply\GetPonyData()
	return unless data
	data\GetRenderController()\PostDrawArms(arms)
	arms.__ppm2_draw = false

IN_DRAW = false
MARKED_FOR_DRAW = {}

PPM2.PreDrawOpaqueRenderables = (bDrawingDepth, bDrawingSkybox) ->
	return if IN_DRAW or PPM2.__RENDERING_REFLECTIONS

	MARKED_FOR_DRAW = {}

	if not ALTERNATIVE_RENDER\GetBool()
		for ply in *player.GetAll()
			if not IsDormant(ply)
				p = IsPony(ply)
				ply.__cachedIsPony = p
				if p
					data = GetPonyData(ply)
					if data
						renderController = data\GetRenderController()
						if renderController
							renderController\PreDraw()
							table.insert(MARKED_FOR_DRAW, renderController)

	if bDrawingDepth and DRAW_LEGS_DEPTH\GetBool()
		with LocalPlayer()
			if .__cachedIsPony and \Alive()
				if data = \GetPonyData()
					IN_DRAW = true
					data\GetRenderController()\DrawLegsDepth()
					IN_DRAW = false

	return if bDrawingDepth or bDrawingSkybox

	if not LEGS_RENDER_TYPE\GetBool()
		with LocalPlayer()
			if .__cachedIsPony and \Alive()
				if data = \GetPonyData()
					IN_DRAW = true
					data\GetRenderController()\DrawLegs()
					IN_DRAW = false

PPM2.PostDrawTranslucentRenderables = (bDrawingDepth, bDrawingSkybox) ->
	if not ALTERNATIVE_RENDER\GetBool() and not bDrawingDepth and not bDrawingSkybox
		for draw in *MARKED_FOR_DRAW
			draw\PostDraw()

PPM2.PostDrawOpaqueRenderables = (bDrawingDepth, bDrawingSkybox) ->
	return if IN_DRAW or PPM2.__RENDERING_REFLECTIONS

	if bDrawingDepth and DRAW_LEGS_DEPTH\GetBool()
		with LocalPlayer()
			if .__cachedIsPony and \Alive()
				if data = \GetPonyData()
					IN_DRAW = true
					data\GetRenderController()\DrawLegsDepth()
					IN_DRAW = false

	return if bDrawingDepth or bDrawingSkybox

	if not TASK_RENDER_TYPE\GetBool()
		for task in *PPM2.NetworkedPonyData.RenderTasks
			ent = task.ent
			if IsValid(ent)
				if ent.__cachedIsPony
					ent\SetNoDraw(true)
					ent.__ppm2_task_hit = true
					renderController = task\GetRenderController()
					renderController\PreDraw(ent)
					IN_DRAW = true
					ent\DrawModel()
					IN_DRAW = false
					renderController\PostDraw(ent)
				else
					if ent.__ppm2_task_hit
						ent.__ppm2_task_hit = false
						ent\SetNoDraw(false)
						task\Reset()

	if not ENABLE_NEW_RAGDOLLS\GetBool()
		for ply in *player.GetAll()
			alive = ply\Alive()
			ply.__ppm2_last_dead = RealTimeL() + 2 if not alive
			if ply.__cachedIsPony
				if ply\GetPonyData() and not alive
					data = ply\GetPonyData()
					rag = ply\GetRagdollEntity()
					if IsValid(rag)
						renderController = data\GetRenderController()
						data\DoRagdollMerge()
						if renderController
							renderController\PreDraw(rag)
							IN_DRAW = true
							rag\DrawModel()
							IN_DRAW = false
							renderController\PostDraw(rag)

	if LEGS_RENDER_TYPE\GetBool()
		with LocalPlayer()
			if .__cachedIsPony and \Alive()
				if data = \GetPonyData()
					IN_DRAW = true
					data\GetRenderController()\DrawLegs()
					IN_DRAW = false

Think = ->
	if TASK_RENDER_TYPE\GetBool()
		for task in *PPM2.NetworkedPonyData.RenderTasks
			ent = task.ent
			if IsValid(ent) and ent.__cachedIsPony
				if ent.__ppm2_task_hit
					ent.__ppm2_task_hit = false
					ent\SetNoDraw(false)

				if not ent.__ppm2RenderOverride
					ent = ent\GetEntity()
					ent.__ppm2_oldRenderOverride = ent.RenderOverride
					ent.__ppm2RenderOverride = ->
						renderController = task\GetRenderController()
						renderController\PreDraw(ent, true)
						ent\DrawModel()
						renderController\PostDraw(ent, true)
						ent.__ppm2_oldRenderOverride(ent) if ent.__ppm2_oldRenderOverride
					ent.RenderOverride = ent.__ppm2RenderOverride

PPM2.PrePlayerDraw = =>
	return if PPM2.__RENDERING_REFLECTIONS
	if ALTERNATIVE_RENDER\GetBool()
		with data = GetPonyData(@)
			return if not data
			@__cachedIsPony = IsPony(@)
			return if not @__cachedIsPony
			f = FrameNumberL()
			return if @__ppm2_last_draw == f
			@__ppm2_last_draw = f
			return if IsDormant(@)
			@__ppm2_last_dead = @__ppm2_last_dead or 0
			return if @__ppm2_last_dead > RealTimeL()
			bones = PPMBonesModifier(@)
			if data and bones\CanThink()
				@ResetBoneManipCache()
				bones\ResetBones()
				hook.Call('PPM2.SetupBones', nil, StrongEntity(@), data) if data
				bones\Think()
				@ApplyBoneManipulations()
				@_ppmBonesModified = true
			renderController = data\GetRenderController()
			status = renderController\PreDraw() if renderController
	else
		with data = GetPonyData(@)
			bones = PPMBonesModifier(@)
			if data and bones\CanThink()
				@ResetBoneManipCache()
				bones\ResetBones()
				hook.Call('PPM2.SetupBones', nil, StrongEntity(@), data) if data
				bones\Think()
				@ApplyBoneManipulations()
				@_ppmBonesModified = true

PPM2.PostPlayerDraw = =>
	return if not ALTERNATIVE_RENDER\GetBool() or PPM2.__RENDERING_REFLECTIONS
	with data = GetPonyData(@)
		return if not data or not @__cachedIsPony
		renderController = data\GetRenderController()
		renderController\PostDraw() if renderController

do
	hornGlowStatus = {}
	smokeMaterial = 'ppm2/hornsmoke'
	fireMat = 'particle/fire'
	hornShift = Vector(1, 0.15, 14.5)

	hook.Add 'PreDrawHalos', 'PPM2.HornEffects', =>
		return if not HORN_HIDE_BEAM\GetBool()
		frame = FrameNumberL()
		cTime = (RealTimeL() % 20) * 4
		for ent, status in pairs hornGlowStatus
			if IsValid(ent) and status.frame == frame and IsValid(status.target)
				additional = math.sin(cTime / 2 + status.haloSeed * 3) * 40
				newCol = DLib.AddColor(status.color, Color(additional, additional, additional))
				halo.Add({status.target}, newCol, math.sin(cTime + status.haloSeed) * 4 + 8, math.cos(cTime + status.haloSeed) * 4 + 8, 2)

	hook.Add 'Think', 'PPM2.HornEffects', =>
		frame = FrameNumberL()
		for ent, status in pairs hornGlowStatus
			if not IsValid(ent)
				status.emmiter\Finish() if IsValid(status.emmiter)
				status.emmiterProp\Finish() if IsValid(status.emmiterProp)
				hornGlowStatus[ent] = nil
			elseif status.frame ~= frame
				status.data\SetHornGlow(status.prevStatus)
				status.emmiter\Finish() if IsValid(status.emmiter)
				status.emmiterProp\Finish() if IsValid(status.emmiterProp)
				hornGlowStatus[ent] = nil
			else
				if not status.prevStatus and RENDER_HORN_GLOW\GetBool() and status.data\GetHornGlow() ~= status.isEnabled
					status.data\SetHornGlow(status.isEnabled)
				if status.attach and IsValid(status.target)
					grabHornPos = Vector(hornShift) * status.data\GetPonySize()
					{:Pos, :Ang} = ent\GetAttachment(status.attach)
					grabHornPos\Rotate(Ang)
					if status.isEnabled and IsValid(status.emmiter) and status.nextSmokeParticle < RealTimeL()
						status.nextSmokeParticle = RealTimeL() + math.Rand(0.1, 0.2)
						for i = 1, math.random(1, 4)
							vec = VectorRand()
							calcPos = Pos + grabHornPos + vec
							with particle = status.emmiter\Add(smokeMaterial, calcPos)
								\SetRollDelta(math.rad(math.random(0, 360)))
								\SetPos(calcPos)
								life = math.Rand(0.6, 0.9)
								\SetStartAlpha(math.random(80, 170))
								\SetDieTime(life)
								\SetColor(status.color.r, status.color.g, status.color.b)
								\SetEndAlpha(0)
								size = math.Rand(2, 3)
								\SetEndSize(math.Rand(2, size))
								\SetStartSize(size)
								\SetGravity(Vector())
								\SetAirResistance(10)
								vecRand = VectorRand()
								vecRand.z *= 2
								\SetVelocity(ent\GetVelocity() + vecRand * status.data\GetPonySize() * 2)
								\SetCollide(false)
					if status.isEnabled and IsValid(status.emmiterProp) and status.nextGrabParticle < RealTimeL() and status.mins and status.maxs
						status.nextGrabParticle = RealTimeL() + math.Rand(0.05, 0.3)
						status.emmiterProp\SetPos(status.tpos)
						for i = 1, math.random(2, 6)
							calcPos = Vector(math.Rand(status.mins.x, status.maxs.x), math.Rand(status.mins.y, status.maxs.y), math.Rand(status.mins.z, status.maxs.z))
							with particle = status.emmiterProp\Add(fireMat, calcPos)
								\SetRollDelta(math.rad(math.random(0, 360)))
								\SetPos(calcPos)
								life = math.Rand(0.5, 0.9)
								\SetStartAlpha(math.random(130, 230))
								\SetDieTime(life)
								\SetColor(status.color.r, status.color.g, status.color.b)
								\SetEndAlpha(0)
								\SetEndSize(0)
								\SetStartSize(math.Rand(2, 6))
								\SetGravity(Vector())
								\SetAirResistance(15)
								\SetVelocity(VectorRand() * 6)
								\SetCollide(false)

	hook.Add 'DrawPhysgunBeam', 'PPM2.HornEffects', (physgun = NULL, isEnabled = false, target = NULL, bone = 0, hitPos = Vector()) =>
		return if not @IsPony() or not HORN_FP\GetBool() and @ == LocalPlayer() and not @ShouldDrawLocalPlayer()
		data = @GetPonyData()
		return if not data
		return if data\GetRace() ~= PPM2.RACE_UNICORN and data\GetRace() ~= PPM2.RACE_ALICORN
		if not hornGlowStatus[@]
			hornGlowStatus[@] = {
				frame: FrameNumberL()
				prevStatus: data\GetHornGlow()
				:data, :isEnabled, :hitPos, :target, :bone
				tpos: @GetPos()
				attach: @LookupAttachment('eyes')
				nextSmokeParticle: 0
				nextGrabParticle: 0
			}

			with hornGlowStatus[@]
				if HORN_PARTICLES\GetBool()
					.emmiter = ParticleEmitter(EyePos())
					.emmiterProp = ParticleEmitter(EyePos())

				.color = data\GetHornMagicColor()
				.haloSeed = math.rad(math.random(-1000, 1000))

				if not data\GetSeparateMagicColor()
					if not data\GetSeparateEyes()
						.color = DLib.LerpColor(0.5, data\GetEyeIrisTop(), data\GetEyeIrisBottom())
					else
						lerpLeft = DLib.LerpColor(0.5, data\GetEyeIrisTopLeft(), data\GetEyeIrisBottomLeft())
						lerpRight = DLib.LerpColor(0.5, data\GetEyeIrisTopRight(), data\GetEyeIrisBottomRight())
						.color = DLib.LerpColor(0.5, lerpLeft, lerpRight)
		else
			with hornGlowStatus[@]
				.frame = FrameNumberL()
				.isEnabled = isEnabled
				.target = target
				.bone = bone
				.hitPos = hitPos
				if IsValid(target)
					.tpos = target\GetPos() + hitPos
					center = target\WorldSpaceCenter()
					.center = center
					mins, maxs = target\WorldSpaceAABB()
					.mins = center + (mins - center) * 1.2
					.maxs = center + (maxs - center) * 1.2
		return false if HORN_HIDE_BEAM\GetBool() and IsValid(target)

hook.Add 'PrePlayerDraw', 'PPM2.PlayerDraw', PPM2.PrePlayerDraw, -2
hook.Add 'PostPlayerDraw', 'PPM2.PostPlayerDraw', PPM2.PostPlayerDraw, -2
hook.Add 'PostDrawOpaqueRenderables', 'PPM2.PostDrawOpaqueRenderables', PPM2.PostDrawOpaqueRenderables, -2
hook.Add 'Think', 'PPM2.UpdateRenderTasks', Think, -2
hook.Add 'PreDrawOpaqueRenderables', 'PPM2.PreDrawOpaqueRenderables', PPM2.PreDrawOpaqueRenderables, -2
hook.Add 'PostDrawTranslucentRenderables', 'PPM2.PostDrawTranslucentRenderables', PPM2.PostDrawTranslucentRenderables, -2


what is this

what is this

--
-- Copyright (C) 2017-2020 DBotThePony

-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-- of the Software, and to permit persons to whom the Software is furnished to do so,
-- subject to the following conditions:

-- The above copyright notice and this permission notice shall be included in all copies
-- or substantial portions of the Software.

-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
-- PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
-- FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-- OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-- DEALINGS IN THE SOFTWARE.

PPM2.USE_HIGHRES_BODY = CreateConVar('ppm2_cl_hires_body', '0', {FCVAR_ARCHIVE}, 'Use high resoluation when rendering pony bodies. AFFECTS ONLY TEXTURE COMPILATION TIME (increases lag spike on pony data load)')
PPM2.USE_HIGHRES_TEXTURES = CreateConVar('ppm2_cl_hires_generic', '0', {FCVAR_ARCHIVE}, 'Create 1024x1024 textures instead of 512x512 on texture compiling')
PPM2.FORCE_PRECACHE = CreateConVar('ppm2_cl_force_precache', '0', {FCVAR_ARCHIVE}, 'Force precache render textures instead of loading them when needed')

_Material = Material

Material = (path) ->
	return path if not PPM2.FORCE_PRECACHE\GetBool()
	matNew = _Material(path)
	return matNew

module = {
	BODY_DETAILS: {
		Material('models/ppm2/partrender/body_leggrad1.png')
		Material('models/ppm2/partrender/body_lines1.png')
		Material('models/ppm2/partrender/body_stripes1.png')
		Material('models/ppm2/partrender/body_headstripes1.png')
		Material('models/ppm2/partrender/body_freckles.png')
		Material('models/ppm2/partrender/body_hooves1.png')
		Material('models/ppm2/partrender/body_hooves2.png')
		Material('models/ppm2/partrender/body_headmask1.png')
		Material('models/ppm2/partrender/body_hooves1_crit.png')
		Material('models/ppm2/partrender/body_hooves2_crit.png')
		Material('models/ppm2/partrender/body_spots1.png')
		Material('models/ppm2/partrender/body_robotic.png')
		Material('models/ppm2/partrender/dash-e.png')
		Material('models/ppm2/partrender/eye_scar.png')
		Material('models/ppm2/partrender/eye_wound.png')
		Material('models/ppm2/partrender/body_scar.png')
		Material('models/ppm2/partrender/gear_socks.png')
		Material('models/ppm2/partrender/sharp_hooves.png')
		Material('models/ppm2/partrender/sharp_hooves2.png')
		Material('models/ppm2/partrender/separated_muzzle.png')
		Material('models/ppm2/partrender/eye_scar_left.png')
		Material('models/ppm2/partrender/eye_scar_right.png')
		Material('models/ppm2/partrender/android_albedo.png')
		Material('models/ppm2/partrender/android_paint.png')
		Material('models/ppm2/partrender/android_albedo_strip.png')
		Material('models/ppm2/partrender/android_paint_strip.png')
		Material('models/ppm2/partrender/cow_paint.png')
		Material('models/ppm2/partrender/deer_paint.png')
		Material('models/ppm2/partrender/deer_extended_paint.png')
		Material('models/ppm2/partrender/demonic_paint.png')
		Material('models/ppm2/partrender/ear_inner_paint.png')
		Material('models/ppm2/partrender/zebra_detail_paint.png')
	}

	UPPER_MANE_DETAILS: {
		[4]: {Material('models/ppm2/partrender/upmane_5_mask0.png')}
		[5]: {Material('models/ppm2/partrender/upmane_6_mask0.png')}
		[7]: {Material('models/ppm2/partrender/upmane_8_mask0.png'), Material('models/ppm2/partrender/upmane_8_mask1.png')}
		[8]: {Material('models/ppm2/partrender/upmane_9_mask0.png'), Material('models/ppm2/partrender/upmane_9_mask1.png'), Material('models/ppm2/partrender/upmane_9_mask2.png')}
		[9]: {Material('models/ppm2/partrender/upmane_10_mask0.png')}
		[10]: {Material('models/ppm2/partrender/upmane_11_mask0.png'), Material('models/ppm2/partrender/upmane_11_mask1.png'), Material('models/ppm2/partrender/upmane_11_mask2.png')}
		[11]: {Material('models/ppm2/partrender/upmane_12_mask0.png')}
		[12]: {Material('models/ppm2/partrender/upmane_13_mask0.png')}
		[13]: {Material('models/ppm2/partrender/upmane_14_mask0.png')}
		[14]: {Material('models/ppm2/partrender/upmane_15_mask0.png')}
	}

	LOWER_MANE_DETAILS: {
		[4]: {Material('models/ppm2/partrender/dnmane_5_mask0.png')}
		[7]: {Material('models/ppm2/partrender/dnmane_8_mask0.png'), Material('models/ppm2/partrender/dnmane_8_mask1.png')}
		[8]: {Material('models/ppm2/partrender/dnmane_9_mask0.png'), Material('models/ppm2/partrender/dnmane_9_mask1.png')}
		[9]: {Material('models/ppm2/partrender/dnmane_10_mask0.png'), Material('models/ppm2/partrender/dnmane_10_mask1.png'), Material('models/ppm2/partrender/dnmane_10_mask2.png')}
		[10]: {Material('models/ppm2/partrender/dnmane_11_mask0.png'), Material('models/ppm2/partrender/dnmane_11_mask1.png')}
		[11]: {Material('models/ppm2/partrender/dnmane_12_mask0.png')}
	}

	TAIL_DETAILS: {
		[4]: {Material('models/ppm2/partrender/tail_5_mask0.png')}
		[7]: {Material('models/ppm2/partrender/tail_8_mask0.png'), Material('models/ppm2/partrender/tail_8_mask1.png'), Material('models/ppm2/partrender/tail_8_mask2.png'), Material('models/ppm2/partrender/tail_8_mask3.png'), Material('models/ppm2/partrender/tail_8_mask4.png')}
		[9]: {Material('models/ppm2/partrender/tail_10_mask0.png')}
		[10]: {Material('models/ppm2/partrender/tail_11_mask0.png'), Material('models/ppm2/partrender/tail_11_mask1.png'), Material('models/ppm2/partrender/tail_11_mask2.png')}
		[11]: {Material('models/ppm2/partrender/tail_12_mask0.png'), Material('models/ppm2/partrender/tail_12_mask1.png')}
		[12]: {Material('models/ppm2/partrender/tail_13_mask0.png')}
		[13]: {Material('models/ppm2/partrender/tail_14_mask0.png')}
	}

	SOCKS_PATCHS: {
		'models/props_pony/ppm/ppm_socks/socks_striped_unlit'
		'models/props_pony/ppm2/ppm_socks/custom/geometric1_1.png'
		'models/props_pony/ppm2/ppm_socks/custom/geometric2_1.png'
		'models/props_pony/ppm2/ppm_socks/custom/geometric3_1.png'
		'models/props_pony/ppm2/ppm_socks/custom/geometric4_1.png'
		'models/props_pony/ppm2/ppm_socks/custom/geometric5_1.png'
		'models/props_pony/ppm2/ppm_socks/custom/geometric6_1.png'
		'models/props_pony/ppm2/ppm_socks/custom/geometric7_1.png'
		'models/props_pony/ppm2/ppm_socks/custom/geometric8_1.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/dark1.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers10.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers11.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers12.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers13.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers14.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers15.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers16.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers17.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers18.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers19.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers2.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers20.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers3.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers4.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers5.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers6.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers7.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers8.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers9.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/grey1.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/grey2.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/grey3.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/hearts1.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/hearts2.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/snow1.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/wallpaper1.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/wallpaper2.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/wallpaper3.png'
	}

	SOCKS_DETAILS_PATCHS: {
		[2]: {
			'models/props_pony/ppm2/ppm_socks/custom/geometric1_4.png'
			'models/props_pony/ppm2/ppm_socks/custom/geometric1_5.png'
			'models/props_pony/ppm2/ppm_socks/custom/geometric1_6.png'
		}

		[3]: {
			'models/props_pony/ppm2/ppm_socks/custom/geometric2_3.png'
			'models/props_pony/ppm2/ppm_socks/custom/geometric2_4.png'
		}

		[4]: {
			'models/props_pony/ppm2/ppm_socks/custom/geometric3_2.png'
			'models/props_pony/ppm2/ppm_socks/custom/geometric3_3.png'
			'models/props_pony/ppm2/ppm_socks/custom/geometric3_5.png'
		}

		[5]: {
			'models/props_pony/ppm2/ppm_socks/custom/geometric4_2.png'
			'models/props_pony/ppm2/ppm_socks/custom/geometric4_3.png'
			'models/props_pony/ppm2/ppm_socks/custom/geometric4_4.png'
		}

		[6]: {
			'models/props_pony/ppm2/ppm_socks/custom/geometric5_4.png'
			'models/props_pony/ppm2/ppm_socks/custom/geometric5_5.png'
			'models/props_pony/ppm2/ppm_socks/custom/geometric5_6.png'
		}

		[7]: {
			'models/props_pony/ppm2/ppm_socks/custom/geometric6_2.png'
			'models/props_pony/ppm2/ppm_socks/custom/geometric6_3.png'
			'models/props_pony/ppm2/ppm_socks/custom/geometric6_4.png'
		}

		[8]: {
			'models/props_pony/ppm2/ppm_socks/custom/geometric7_3.png'
			'models/props_pony/ppm2/ppm_socks/custom/geometric7_4.png'
		}

		[9]: {
			'models/props_pony/ppm2/ppm_socks/custom/geometric8_2.png'
			'models/props_pony/ppm2/ppm_socks/custom/geometric8_3.png'
		}
	}
}

additionTable = (...) ->
	tab = {'$ignorez': 1, '$vertexcolor': 1, '$vertexalpha': 1, '$nolod': 1}
	args = {...}
	for i = 1, #args, 2
		key, val = args[i], args[i + 1]
		tab[key] = val
	return tab

module.SOCKS_MATERIALS = [Material(id) for _, id in ipairs module.SOCKS_PATCHS]
module.SOCKS_DETAILS = {i, [Material(path) for path in *data] for i, data in pairs module.SOCKS_DETAILS_PATCHS}
module.CUTIEMARKS = [Material("models/ppm2/cmarks/#{mark}.png") for _, mark in ipairs PPM2.DefaultCutiemarks]
module.SUITS = [Material("models/ppm2/texclothes/#{mat}.png") for _, mat in ipairs {
	'clothes_royalguard', 'clothes_sbs_full'
	'clothes_sbs_light', 'clothes_wbs_full'
	'clothes_wbs_light', 'spidermane_light'
	'spidermane_full'
}]

module.TATTOOS = [Material("models/ppm2/partrender/tattoo/#{fil\lower()}.png") for _, fil in ipairs PPM2.TATTOOS_REGISTRY when fil ~= 'NONE']
debugwhite = {
	'$basetexture': 'models/debug/debugwhite'
	'$ignorez': 1
	'$vertexcolor': 1
	'$vertexalpha': 1
	'$nolod': 1
}

module.EYE_OVALS = {
	Material('models/ppm2/partrender/eye_oval.png')
	Material('models/ppm2/partrender/eye_oval_aperture.png')
}

module.EYE_REFLECTIONS = {
	_Material('models/ppm2/partrender/eye_reflection.png')
	_Material('models/ppm2/partrender/eye_reflection_crystal_foal.png')
	_Material('models/ppm2/partrender/eye_reflection_crystal_unisex.png')
	_Material('models/ppm2/partrender/eye_reflection_foal.png')
	_Material('models/ppm2/partrender/eye_reflection_male.png')
}

module.HEAD_CLOTHES = {
	[2]: {
		Material('models/ppm2/clothesrender/hat_aj.png')
	}

	[3]: {
		Material('models/ppm2/clothesrender/hat_braeburn_2.png')
	}

	[4]: {
		Material('models/ppm2/clothesrender/tr_hat_stars_1.png')
		Material('models/ppm2/clothesrender/tr_hat_stars_2.png')
	}

	[5]: {
		Material('models/ppm2/clothesrender/headphones_1.png')
		Material('models/ppm2/clothesrender/headphones_2.png')
		Material('models/ppm2/clothesrender/headphones_note.png')
	}
}

module.HEAD_CLOTHES_INDEX = {
	[2]: 0
	[3]: 1
	[4]: 2
	[5]: 3
}

module.NECK_CLOTHES = {
	[2]: {
		Material('models/ppm2/clothesrender/winter_scarf_1.png')
		Material('models/ppm2/clothesrender/winter_scarf_2.png')
	}

	[3]: {
		{
			Material('models/ppm2/clothesrender/cape_stars_1.png')
			Material('models/ppm2/clothesrender/cape_stars_2.png')
		}
		{
			Material('models/ppm2/clothesrender/gem.png')
		}
	}

	[4]: {
		Material('models/ppm2/clothesrender/tie_1.png')
		Material('models/ppm2/clothesrender/tie_2.png')
	}

	[5]: {
		Material('models/ppm2/clothesrender/bowtie_1.png')
		Material('models/ppm2/clothesrender/bowtie_2.png')
	}
}

module.NECK_CLOTHES_INDEX = {
	[2]: 4
	[3]: {5, 6}
	[4]: 7
	[5]: 8
}

module.BODY_CLOTHES = {
	[2]: {
		Material('models/ppm2/clothesrender/vest_pouches.png')
		Material('models/ppm2/clothesrender/vest_string.png')
	}

	[3]: {
		Material('models/ppm2/clothesrender/shirt.png')
	}

	[4]: {
		Material('models/ppm2/clothesrender/hoodie.png')
	}

	[5]: {
		Material('models/ppm2/clothesrender/badge.png')
	}
}

module.BODY_CLOTHES_INDEX = {
	[2]: 9
	[3]: 10
	[4]: 11
	[5]: 12
}

module.EYE_CLOTHES = {
	[2]: {
		{}
		{Material('models/ppm2/clothesrender/lense.png')}
	}
	[4]: {
		{}
		{Material('models/ppm2/clothesrender/shades_lense.png')}
	}
	[6]: {
		{}
		{}
	}
	[8]: {
		{}
		{}
	}
}

module.EYE_CLOTHES[3] = module.EYE_CLOTHES[2]
module.EYE_CLOTHES[5] = module.EYE_CLOTHES[4]
module.EYE_CLOTHES[7] = module.EYE_CLOTHES[6]
module.EYE_CLOTHES[9] = module.EYE_CLOTHES[8]

module.EYE_CLOTHES_INDEX = {
	[2]: {13, 14}
	[4]: {15, 16}
	[6]: {17, 18}
	[8]: {20, 21}
}

module.EYE_CLOTHES_INDEX[3] = module.EYE_CLOTHES_INDEX[2]
module.EYE_CLOTHES_INDEX[5] = module.EYE_CLOTHES_INDEX[4]
module.EYE_CLOTHES_INDEX[7] = module.EYE_CLOTHES_INDEX[6]
module.EYE_CLOTHES_INDEX[9] = module.EYE_CLOTHES_INDEX[8]

do
	_prMaterial = (tab) ->
		for i, sub in pairs(tab)
			if not istable(sub[1])
				tab[i] = {}
				tab[i][1] = [mat for mat in *sub when not isnumber(mat)]

	_prIndex = (tab) ->
		for i, sub in pairs(tab)
			if not istable(sub)
				tab[i] = {sub}

	_prMaterial(module.HEAD_CLOTHES)
	_prIndex(module.HEAD_CLOTHES_INDEX)
	_prMaterial(module.NECK_CLOTHES)
	_prIndex(module.NECK_CLOTHES_INDEX)
	_prMaterial(module.BODY_CLOTHES)
	_prIndex(module.BODY_CLOTHES_INDEX)
	_prMaterial(module.EYE_CLOTHES)
	_prIndex(module.EYE_CLOTHES_INDEX)

module.DEBUGWHITE = CreateMaterial('PPM2.Debugwhite', 'UnlitGeneric', debugwhite)
module.HAIR_MATERIAL_COLOR = CreateMaterial('PPM2.ManeTextureBase', 'UnlitGeneric', debugwhite)
module.TAIL_MATERIAL_COLOR = CreateMaterial('PPM2.TailTextureBase', 'UnlitGeneric', debugwhite)
module.WINGS_MATERIAL_COLOR = CreateMaterial('PPM2.WingsMaterialBase', 'UnlitGeneric', debugwhite)
module.HORN_MATERIAL_COLOR = CreateMaterial('PPM2.HornMaterialBase', 'UnlitGeneric', additionTable('$basetexture', 'models/ppm2/base/horn'))
module.BODY_MATERIAL = CreateMaterial('PPM2.BodyTexture', 'UnlitGeneric', additionTable('$basetexture', 'models/ppm2/base/body'))
module.HORN_DETAIL_BUMP = CreateMaterial('PPM2.HornBumpMapRenderer', 'UnlitGeneric', additionTable('$basetexture', 'models/ppm2/base/horn_normal'))
module.HORN_DETAIL_COLOR = Material('models/ppm2/partrender/horn_detail.png')
module.EYE_OVAL = Material('models/ppm2/partrender/eye_oval.png')
module.EYE_GRAD = Material('models/ppm2/partrender/eye_grad.png')
module.EYE_EFFECT = Material('models/ppm2/partrender/eye_effect.png')
module.EYE_LINE_L_1 = Material('models/ppm2/partrender/eye_line_l1.png')
module.EYE_LINE_R_1 = Material('models/ppm2/partrender/eye_line_r1.png')
module.EYE_LINE_L_2 = Material('models/ppm2/partrender/eye_line_l2.png')
module.EYE_LINE_R_2 = Material('models/ppm2/partrender/eye_line_r2.png')
module.EYEBROWS = Material('models/ppm2/partrender/eyebrows.png')
module.PONY_SOCKS = Material('models/ppm2/texclothes/pony_socks.png')

module.LIPS = Material('models/ppm2/partrender/lips.png')
module.NOSE = Material('models/ppm2/partrender/nose.png')
module.BODY_BUMP = Material('models/ppm2/partrender/body_bump2.png')

module.EYE_CORNERA = Material('models/ppm2/eyes/eye_cornea')
module.EYE_CORNERA_OVAL = Material('models/ppm2/eyes/eye_cornea_oval')
module.EYE_EXTRA = Material('models/ppm2/eyes/eye_extra')
module.EYE_EXTRA2 = Material('models/ppm2/eyes/eye_extra2')
module.EYE_LIGHTWARP = Material('models/ppm2/eyes/eye_lightwarp')
module.EYE_REFLECTION2 = Material('models/ppm2/eyes/eye_reflection')

module.MAGIC_HANDS_MATERIAL = Material('models/ppm2/base/magic_arms')

__index = (key) =>
	if key == 'size'
		return #getmetatable(@).original

	value = rawget(getmetatable(@).original, key)
	return value if not isstring(value)
	mat = _Material(value, 'smooth')
	table.insert(RELOADABLE_MATERIALS, mat)
	rawset(@, key, mat)
	return mat

patch = (input) ->
	patch(value) for key, value in pairs(input) when istable(value)

	meta = {
		__index: __index
		original: table.Copy(input)
	}

	for k in *table.GetKeys(input)
		if not istable(input[k])
			input[k] = nil

	return setmetatable(input, meta)

PPM2.MaterialsRegistry = patch(module)

return module

--
-- Copyright (C) 2017-2020 DBotThePony

-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-- of the Software, and to permit persons to whom the Software is furnished to do so,
-- subject to the following conditions:

-- The above copyright notice and this permission notice shall be included in all copies
-- or substantial portions of the Software.

-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
-- PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
-- FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-- OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-- DEALINGS IN THE SOFTWARE.

PPM2.USE_HIGHRES_BODY = CreateConVar('ppm2_cl_hires_body', '0', {FCVAR_ARCHIVE}, 'Use high resoluation when rendering pony bodies. AFFECTS ONLY TEXTURE COMPILATION TIME (increases lag spike on pony data load)')
PPM2.USE_HIGHRES_TEXTURES = CreateConVar('ppm2_cl_hires_generic', '0', {FCVAR_ARCHIVE}, 'Create 1024x1024 textures instead of 512x512 on texture compiling')
PPM2.FORCE_PRECACHE = CreateConVar('ppm2_cl_force_precache', '0', {FCVAR_ARCHIVE}, 'Force precache render textures instead of loading them when needed')

_Material = Material

RELOADABLE_MATERIALS = {}
PPM2.RELOADABLE_MATERIALS = RELOADABLE_MATERIALS

concommand.Add 'ppm2_reload_materials', ->
	cTime = SysTime()
	for _, mat in ipairs RELOADABLE_MATERIALS
		if texname = mat\GetString('$basetexture')
			mat\SetTexture('$basetexture', texname)
		if texture = mat\GetTexture('$basetexture')
			texture\Download()
		mat\Recompute()
	PPM2.PonyTextureController.URL_MATERIAL_CACHE = {}
	PPM2.Message('Reloaded textures in ', math.floor((SysTime() - cTime) * 100000) / 100, ' milliseconds.')
	RunConsoleCommand('ppm2_reload')
	RunConsoleCommand('ppm2_require')

_CreateMaterial = CreateMaterial

Material = (path) ->
	return path if not PPM2.FORCE_PRECACHE\GetBool()
	matNew = _Material(path)
	table.insert(RELOADABLE_MATERIALS, matNew)
	return matNew

CreateMaterial = (name, shader, data) ->
	matNew, time = _CreateMaterial(name, shader, data)
	table.insert(RELOADABLE_MATERIALS, matNew)
	return matNew, time

module = {
	BODY_DETAILS: {
		Material('models/ppm2/partrender/body_leggrad1.png')
		Material('models/ppm2/partrender/body_lines1.png')
		Material('models/ppm2/partrender/body_stripes1.png')
		Material('models/ppm2/partrender/body_headstripes1.png')
		Material('models/ppm2/partrender/body_freckles.png')
		Material('models/ppm2/partrender/body_hooves1.png')
		Material('models/ppm2/partrender/body_hooves2.png')
		Material('models/ppm2/partrender/body_headmask1.png')
		Material('models/ppm2/partrender/body_hooves1_crit.png')
		Material('models/ppm2/partrender/body_hooves2_crit.png')
		Material('models/ppm2/partrender/body_spots1.png')
		Material('models/ppm2/partrender/body_robotic.png')
		Material('models/ppm2/partrender/dash-e.png')
		Material('models/ppm2/partrender/eye_scar.png')
		Material('models/ppm2/partrender/eye_wound.png')
		Material('models/ppm2/partrender/body_scar.png')
		Material('models/ppm2/partrender/gear_socks.png')
		Material('models/ppm2/partrender/sharp_hooves.png')
		Material('models/ppm2/partrender/sharp_hooves2.png')
		Material('models/ppm2/partrender/separated_muzzle.png')
		Material('models/ppm2/partrender/eye_scar_left.png')
		Material('models/ppm2/partrender/eye_scar_right.png')
		Material('models/ppm2/partrender/android_albedo.png')
		Material('models/ppm2/partrender/android_paint.png')
		Material('models/ppm2/partrender/android_albedo_strip.png')
		Material('models/ppm2/partrender/android_paint_strip.png')
		Material('models/ppm2/partrender/cow_paint.png')
		Material('models/ppm2/partrender/deer_paint.png')
		Material('models/ppm2/partrender/deer_extended_paint.png')
		Material('models/ppm2/partrender/demonic_paint.png')
		Material('models/ppm2/partrender/ear_inner_paint.png')
		Material('models/ppm2/partrender/zebra_detail_paint.png')
	}

	UPPER_MANE_DETAILS: {
		[4]: {Material('models/ppm2/partrender/upmane_5_mask0.png')}
		[5]: {Material('models/ppm2/partrender/upmane_6_mask0.png')}
		[7]: {Material('models/ppm2/partrender/upmane_8_mask0.png'), Material('models/ppm2/partrender/upmane_8_mask1.png')}
		[8]: {Material('models/ppm2/partrender/upmane_9_mask0.png'), Material('models/ppm2/partrender/upmane_9_mask1.png'), Material('models/ppm2/partrender/upmane_9_mask2.png')}
		[9]: {Material('models/ppm2/partrender/upmane_10_mask0.png')}
		[10]: {Material('models/ppm2/partrender/upmane_11_mask0.png'), Material('models/ppm2/partrender/upmane_11_mask1.png'), Material('models/ppm2/partrender/upmane_11_mask2.png')}
		[11]: {Material('models/ppm2/partrender/upmane_12_mask0.png')}
		[12]: {Material('models/ppm2/partrender/upmane_13_mask0.png')}
		[13]: {Material('models/ppm2/partrender/upmane_14_mask0.png')}
		[14]: {Material('models/ppm2/partrender/upmane_15_mask0.png')}
	}

	LOWER_MANE_DETAILS: {
		[4]: {Material('models/ppm2/partrender/dnmane_5_mask0.png')}
		[7]: {Material('models/ppm2/partrender/dnmane_8_mask0.png'), Material('models/ppm2/partrender/dnmane_8_mask1.png')}
		[8]: {Material('models/ppm2/partrender/dnmane_9_mask0.png'), Material('models/ppm2/partrender/dnmane_9_mask1.png')}
		[9]: {Material('models/ppm2/partrender/dnmane_10_mask0.png'), Material('models/ppm2/partrender/dnmane_10_mask1.png'), Material('models/ppm2/partrender/dnmane_10_mask2.png')}
		[10]: {Material('models/ppm2/partrender/dnmane_11_mask0.png'), Material('models/ppm2/partrender/dnmane_11_mask1.png')}
		[11]: {Material('models/ppm2/partrender/dnmane_12_mask0.png')}
	}

	TAIL_DETAILS: {
		[4]: {Material('models/ppm2/partrender/tail_5_mask0.png')}
		[7]: {Material('models/ppm2/partrender/tail_8_mask0.png'), Material('models/ppm2/partrender/tail_8_mask1.png'), Material('models/ppm2/partrender/tail_8_mask2.png'), Material('models/ppm2/partrender/tail_8_mask3.png'), Material('models/ppm2/partrender/tail_8_mask4.png')}
		[9]: {Material('models/ppm2/partrender/tail_10_mask0.png')}
		[10]: {Material('models/ppm2/partrender/tail_11_mask0.png'), Material('models/ppm2/partrender/tail_11_mask1.png'), Material('models/ppm2/partrender/tail_11_mask2.png')}
		[11]: {Material('models/ppm2/partrender/tail_12_mask0.png'), Material('models/ppm2/partrender/tail_12_mask1.png')}
		[12]: {Material('models/ppm2/partrender/tail_13_mask0.png')}
		[13]: {Material('models/ppm2/partrender/tail_14_mask0.png')}
	}

	SOCKS_PATCHS: {
		'models/props_pony/ppm/ppm_socks/socks_striped_unlit'
		'models/props_pony/ppm2/ppm_socks/custom/geometric1_1.png'
		'models/props_pony/ppm2/ppm_socks/custom/geometric2_1.png'
		'models/props_pony/ppm2/ppm_socks/custom/geometric3_1.png'
		'models/props_pony/ppm2/ppm_socks/custom/geometric4_1.png'
		'models/props_pony/ppm2/ppm_socks/custom/geometric5_1.png'
		'models/props_pony/ppm2/ppm_socks/custom/geometric6_1.png'
		'models/props_pony/ppm2/ppm_socks/custom/geometric7_1.png'
		'models/props_pony/ppm2/ppm_socks/custom/geometric8_1.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/dark1.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers10.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers11.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers12.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers13.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers14.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers15.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers16.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers17.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers18.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers19.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers2.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers20.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers3.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers4.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers5.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers6.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers7.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers8.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/flowers9.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/grey1.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/grey2.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/grey3.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/hearts1.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/hearts2.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/snow1.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/wallpaper1.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/wallpaper2.png'
		'models/props_pony/ppm2/ppm_socks/custom_textured/wallpaper3.png'
	}

	SOCKS_DETAILS_PATCHS: {
		[2]: {
			'models/props_pony/ppm2/ppm_socks/custom/geometric1_4.png'
			'models/props_pony/ppm2/ppm_socks/custom/geometric1_5.png'
			'models/props_pony/ppm2/ppm_socks/custom/geometric1_6.png'
		}

		[3]: {
			'models/props_pony/ppm2/ppm_socks/custom/geometric2_3.png'
			'models/props_pony/ppm2/ppm_socks/custom/geometric2_4.png'
		}

		[4]: {
			'models/props_pony/ppm2/ppm_socks/custom/geometric3_2.png'
			'models/props_pony/ppm2/ppm_socks/custom/geometric3_3.png'
			'models/props_pony/ppm2/ppm_socks/custom/geometric3_5.png'
		}

		[5]: {
			'models/props_pony/ppm2/ppm_socks/custom/geometric4_2.png'
			'models/props_pony/ppm2/ppm_socks/custom/geometric4_3.png'
			'models/props_pony/ppm2/ppm_socks/custom/geometric4_4.png'
		}

		[6]: {
			'models/props_pony/ppm2/ppm_socks/custom/geometric5_4.png'
			'models/props_pony/ppm2/ppm_socks/custom/geometric5_5.png'
			'models/props_pony/ppm2/ppm_socks/custom/geometric5_6.png'
		}

		[7]: {
			'models/props_pony/ppm2/ppm_socks/custom/geometric6_2.png'
			'models/props_pony/ppm2/ppm_socks/custom/geometric6_3.png'
			'models/props_pony/ppm2/ppm_socks/custom/geometric6_4.png'
		}

		[8]: {
			'models/props_pony/ppm2/ppm_socks/custom/geometric7_3.png'
			'models/props_pony/ppm2/ppm_socks/custom/geometric7_4.png'
		}

		[9]: {
			'models/props_pony/ppm2/ppm_socks/custom/geometric8_2.png'
			'models/props_pony/ppm2/ppm_socks/custom/geometric8_3.png'
		}
	}
}

additionTable = (...) ->
	tab = {'$ignorez': 1, '$vertexcolor': 1, '$vertexalpha': 1, '$nolod': 1}
	args = {...}
	for i = 1, #args, 2
		key, val = args[i], args[i + 1]
		tab[key] = val
	return tab

module.SOCKS_MATERIALS = [Material(id) for _, id in ipairs module.SOCKS_PATCHS]
module.SOCKS_DETAILS = {i, [Material(path) for path in *data] for i, data in pairs module.SOCKS_DETAILS_PATCHS}
module.CUTIEMARKS = [Material("models/ppm2/cmarks/#{mark}.png") for _, mark in ipairs PPM2.DefaultCutiemarks]
module.SUITS = [Material("models/ppm2/texclothes/#{mat}.png") for _, mat in ipairs {
	'clothes_royalguard', 'clothes_sbs_full'
	'clothes_sbs_light', 'clothes_wbs_full'
	'clothes_wbs_light', 'spidermane_light'
	'spidermane_full'
}]

module.TATTOOS = [Material("models/ppm2/partrender/tattoo/#{fil\lower()}.png") for _, fil in ipairs PPM2.TATTOOS_REGISTRY when fil ~= 'NONE']
debugwhite = {
	'$basetexture': 'models/debug/debugwhite'
	'$ignorez': 1
	'$vertexcolor': 1
	'$vertexalpha': 1
	'$nolod': 1
}

module.EYE_OVALS = {
	Material('models/ppm2/partrender/eye_oval.png')
	Material('models/ppm2/partrender/eye_oval_aperture.png')
}

module.EYE_REFLECTIONS = {
	_Material('models/ppm2/partrender/eye_reflection.png')
	_Material('models/ppm2/partrender/eye_reflection_crystal_foal.png')
	_Material('models/ppm2/partrender/eye_reflection_crystal_unisex.png')
	_Material('models/ppm2/partrender/eye_reflection_foal.png')
	_Material('models/ppm2/partrender/eye_reflection_male.png')
}

module.HEAD_CLOTHES = {
	[2]: {
		Material('models/ppm2/clothesrender/hat_aj.png')
	}

	[3]: {
		Material('models/ppm2/clothesrender/hat_braeburn_2.png')
	}

	[4]: {
		Material('models/ppm2/clothesrender/tr_hat_stars_1.png')
		Material('models/ppm2/clothesrender/tr_hat_stars_2.png')
	}

	[5]: {
		Material('models/ppm2/clothesrender/headphones_1.png')
		Material('models/ppm2/clothesrender/headphones_2.png')
		Material('models/ppm2/clothesrender/headphones_note.png')
	}
}

module.HEAD_CLOTHES_INDEX = {
	[2]: 0
	[3]: 1
	[4]: 2
	[5]: 3
}

module.NECK_CLOTHES = {
	[2]: {
		Material('models/ppm2/clothesrender/winter_scarf_1.png')
		Material('models/ppm2/clothesrender/winter_scarf_2.png')
	}

	[3]: {
		{
			Material('models/ppm2/clothesrender/cape_stars_1.png')
			Material('models/ppm2/clothesrender/cape_stars_2.png')
		}
		{
			Material('models/ppm2/clothesrender/gem.png')
		}
	}

	[4]: {
		Material('models/ppm2/clothesrender/tie_1.png')
		Material('models/ppm2/clothesrender/tie_2.png')
	}

	[5]: {
		Material('models/ppm2/clothesrender/bowtie_1.png')
		Material('models/ppm2/clothesrender/bowtie_2.png')
	}
}

module.NECK_CLOTHES_INDEX = {
	[2]: 4
	[3]: {5, 6}
	[4]: 7
	[5]: 8
}

module.BODY_CLOTHES = {
	[2]: {
		Material('models/ppm2/clothesrender/vest_pouches.png')
		Material('models/ppm2/clothesrender/vest_string.png')
	}

	[3]: {
		Material('models/ppm2/clothesrender/shirt.png')
	}

	[4]: {
		Material('models/ppm2/clothesrender/hoodie.png')
	}

	[5]: {
		Material('models/ppm2/clothesrender/badge.png')
	}
}

module.BODY_CLOTHES_INDEX = {
	[2]: 9
	[3]: 10
	[4]: 11
	[5]: 12
}

module.EYE_CLOTHES = {
	[2]: {
		{}
		{Material('models/ppm2/clothesrender/lense.png')}
	}
	[4]: {
		{}
		{Material('models/ppm2/clothesrender/shades_lense.png')}
	}
	[6]: {
		{}
		{}
	}
	[8]: {
		{}
		{}
	}
}

module.EYE_CLOTHES[3] = module.EYE_CLOTHES[2]
module.EYE_CLOTHES[5] = module.EYE_CLOTHES[4]
module.EYE_CLOTHES[7] = module.EYE_CLOTHES[6]
module.EYE_CLOTHES[9] = module.EYE_CLOTHES[8]

module.EYE_CLOTHES_INDEX = {
	[2]: {13, 14}
	[4]: {15, 16}
	[6]: {17, 18}
	[8]: {20, 21}
}

module.EYE_CLOTHES_INDEX[3] = module.EYE_CLOTHES_INDEX[2]
module.EYE_CLOTHES_INDEX[5] = module.EYE_CLOTHES_INDEX[4]
module.EYE_CLOTHES_INDEX[7] = module.EYE_CLOTHES_INDEX[6]
module.EYE_CLOTHES_INDEX[9] = module.EYE_CLOTHES_INDEX[8]

do
	_prMaterial = (tab) ->
		for i, sub in pairs(tab)
			if not istable(sub[1])
				tab[i] = {}
				tab[i][1] = [mat for mat in *sub when not isnumber(mat)]

	_prIndex = (tab) ->
		for i, sub in pairs(tab)
			if not istable(sub)
				tab[i] = {sub}

	_prMaterial(module.HEAD_CLOTHES)
	_prIndex(module.HEAD_CLOTHES_INDEX)
	_prMaterial(module.NECK_CLOTHES)
	_prIndex(module.NECK_CLOTHES_INDEX)
	_prMaterial(module.BODY_CLOTHES)
	_prIndex(module.BODY_CLOTHES_INDEX)
	_prMaterial(module.EYE_CLOTHES)
	_prIndex(module.EYE_CLOTHES_INDEX)

module.DEBUGWHITE = CreateMaterial('PPM2.Debugwhite', 'UnlitGeneric', debugwhite)
module.HAIR_MATERIAL_COLOR = CreateMaterial('PPM2.ManeTextureBase', 'UnlitGeneric', debugwhite)
module.TAIL_MATERIAL_COLOR = CreateMaterial('PPM2.TailTextureBase', 'UnlitGeneric', debugwhite)
module.WINGS_MATERIAL_COLOR = CreateMaterial('PPM2.WingsMaterialBase', 'UnlitGeneric', debugwhite)
module.HORN_MATERIAL_COLOR = CreateMaterial('PPM2.HornMaterialBase', 'UnlitGeneric', additionTable('$basetexture', 'models/ppm2/base/horn'))
module.BODY_MATERIAL = CreateMaterial('PPM2.BodyTexture', 'UnlitGeneric', additionTable('$basetexture', 'models/ppm2/base/body'))
module.HORN_DETAIL_BUMP = CreateMaterial('PPM2.HornBumpMapRenderer', 'UnlitGeneric', additionTable('$basetexture', 'models/ppm2/base/horn_normal'))
module.HORN_DETAIL_COLOR = Material('models/ppm2/partrender/horn_detail.png')
module.EYE_OVAL = Material('models/ppm2/partrender/eye_oval.png')
module.EYE_GRAD = Material('models/ppm2/partrender/eye_grad.png')
module.EYE_EFFECT = Material('models/ppm2/partrender/eye_effect.png')
module.EYE_LINE_L_1 = Material('models/ppm2/partrender/eye_line_l1.png')
module.EYE_LINE_R_1 = Material('models/ppm2/partrender/eye_line_r1.png')
module.EYE_LINE_L_2 = Material('models/ppm2/partrender/eye_line_l2.png')
module.EYE_LINE_R_2 = Material('models/ppm2/partrender/eye_line_r2.png')
module.EYEBROWS = Material('models/ppm2/partrender/eyebrows.png')
module.PONY_SOCKS = Material('models/ppm2/texclothes/pony_socks.png')

module.LIPS = Material('models/ppm2/partrender/lips.png')
module.NOSE = Material('models/ppm2/partrender/nose.png')
module.BODY_BUMP = Material('models/ppm2/partrender/body_bump2.png')

module.EYE_CORNERA = Material('models/ppm2/eyes/eye_cornea')
module.EYE_CORNERA_OVAL = Material('models/ppm2/eyes/eye_cornea_oval')
module.EYE_EXTRA = Material('models/ppm2/eyes/eye_extra')
module.EYE_EXTRA2 = Material('models/ppm2/eyes/eye_extra2')
module.EYE_LIGHTWARP = Material('models/ppm2/eyes/eye_lightwarp')
module.EYE_REFLECTION2 = Material('models/ppm2/eyes/eye_reflection')

module.MAGIC_HANDS_MATERIAL = Material('models/ppm2/base/magic_arms')

__index = (key) =>
	if key == 'size'
		return #getmetatable(@).original

	value = rawget(getmetatable(@).original, key)
	return value if not isstring(value)
	mat = _Material(value, 'smooth')
	table.insert(RELOADABLE_MATERIALS, mat)
	rawset(@, key, mat)
	return mat

patch = (input) ->
	patch(value) for key, value in pairs(input) when istable(value)

	meta = {
		__index: __index
		original: table.Copy(input)
	}

	for k in *table.GetKeys(input)
		if not istable(input[k])
			input[k] = nil

	return setmetatable(input, meta)

PPM2.MaterialsRegistry = patch(module)

return module


list: also register for command help. Remove leading ^

Former-commit-id: 3c7a93fe8da2cdef9b991cfe4ca209e20a1d0d31 [formerly 79434c30703cc9dab95656da72dee15fd9fd05b1]
Former-commit-id: b8703b31024a292f6ea09ac326245ffb0566c387

listModules = (source, destination, arg) =>
  out = {}
  for moduleName, moduleTable in next, @Events!['PRIVMSG']
    unless @IsModuleDisabled moduleName, destination
      table.insert out, moduleName
  if #out > 0
    say "Modules: %s", table.concat(out, ' ')

listPatterns = (source, destination, moduleName) =>
  moduleTable = @Events!['PRIVMSG'][moduleName]
  unless moduleTable
    return

  if @IsModuleDisabled moduleName, destination
    return

  out = {}
  for pattern, callback in next, moduleTable
    patt = @ChannelCommandPattern(pattern, moduleName, destination)
    -- Make them prettier
    patt = patt\gsub('%^%%p', '!')
    patt = patt\gsub('%$$', '')
    patt = patt\gsub('^%^', '')
    table.insert out, patt

  if #out > 0
    say "%s patterns: %s", moduleName, table.concat(out, ', ')

PRIVMSG:
  '^%plist$': listModules
  '^%plist (%w+)$': listPatterns
  '^%phelp$': listModules
  '^%phelp (%w+)$': listPatterns

listModules = (source, destination, arg) =>
  out = {}
  for moduleName, moduleTable in next, @Events!['PRIVMSG']
    unless @IsModuleDisabled moduleName, destination
      table.insert out, moduleName
  if #out > 0
    say "Modules: %s", table.concat(out, ' ')

listPatterns = (source, destination, moduleName) =>
  moduleTable = @Events!['PRIVMSG'][moduleName]
  unless moduleTable
    return

  if @IsModuleDisabled moduleName, destination
    return

  out = {}
  for pattern, callback in next, moduleTable
    patt = @ChannelCommandPattern(pattern, moduleName, destination)
    -- Make them prettier
    patt = patt\gsub('%^%%p', '!')
    patt = patt\gsub('%$$', '')
    table.insert out, patt

  if #out > 0
    say "%s patterns: %s", moduleName, table.concat(out, ', ')

PRIVMSG:
  '^%plist$': listModules
  '^%plist (%w+)$': listPatterns


Added some comments to LP

Added some comments to LP

require "LunoPunk.geometry.Point"
require "LunoPunk.geometry.Rectangle"
require "LunoPunk.geometry.Matrix"
require "LunoPunk.Scene"
require "LunoPunk.Tweener"

export ^

class lp
	-- Private
	BASELAYER = 10
	DEG = -180 / math.pi
	INT_MAX_VALUE = nil
	NUMBER_MAX_VALUE = nil
	RAD = math.pi / -180
	VERSION = "0.0.1"
	__scene = nil

	_gameTime: nil
	_renderTime: nil
	_systemTime: nil
	_updateTime: nil
	assignedFrameRate: nil
	blackColor: 0x00000000
	bounds: nil
	buffer: nil
	camera: Point!
	console: nil
	defaultFont: "default"
	elapsed: 0
	engine: nil
	entity: nil
	fixed: false
	focused: nil
	frameRate: 0
	log: nil
	matrix: Matrix!
	orientations: nil
	pan: nil
	point: Point!
	point2: Point!
	random: -> math.random!
	randomSeed: nil
	rate: 0
	rect: Rectangle!
	renderMode = (value) ->
		return __renderMode if value == nil or __renderMode == value
		__renderMode = value

		-- recreate screen for buffer rendering
		if LP.screen == nil
			LP.screen = Screen!
		else
			LP.screen.init!

		value

	scene: Scene!
	screen: nil
	sprite: nil
	tweener: Tweener!
	volume: 1
	watch: nil
	world: nil
	zero: Point!

	-- Window/resolution related fields
	fullscreen: false
	halfHeight: -> LP.height/2
	halfWidth: -> LP.width/2
	height: 0
	width: 0
	windowHeight: 0
	windowWidth: 0

	alarm: (delay, complete, typeweenType, tweener) ->
	anchorTo: (object, anchor, distance) ->
	angle: (x1, y1, x2, y2) ->
	angleDifference: (angle1, angle2) ->
	angleXY: (object, angle, length, x, y) ->

	-- Approaches the value towards the target, by the specified amount, without overshooting the target.
	-- @param	value	The starting value.
	-- @param	target	The target that you want value to approach.
	-- @param	amount	How much you want the value to approach target by.
	-- @return	The new value.
	approach: (value, target, amount) ->
		if value < target - amount
			value + amount
		else if value > target + amount
			value - amount
		else
			target

	choose: (objs) ->

	-- Clamps the value within the minimum and maximum values.
	-- @param	value		The Float to evaluate.
	-- @param	min			The minimum range.
	-- @param	max			The maximum range.
	-- @return	The clamped value.
	clamp: (value, min, max) -> if value < min
			min
		else if value > max
			max
		else
			value

	clampInRect: (object, x, y, width, height, padding) ->
	clear: (t) -> while #t > 0 do table.remove t
	consoleEnabled: ->
	createBitmap: (width, height, transparent, color) ->
	distance: (x1, y1, x2, y2) ->
	istanceRectPoint: (px, py, rx, ry, rw, rh) ->
	distanceRects: (x1, y1, w1, h1, x2, y2, w2, h2) ->
	distanceSquared: (x1, y1, xprint2, y2) ->
	frames: (from_frame, to_frame, skip) ->
	getImage: (source) -> love.graphics.newImage source
	getColorHSV: (h, s, v) ->
	getColorHue: (color) ->

	getColorSaturation: (color) ->
	getColorValue: (color) ->

	-- Take a 12 bit RGB integer and turn it into a table
	convertColor: (color) -> { lp.getRed(color), lp.getGreen(color), lp.getBlue(color) }

	-- Make a 12 bit RGB integer
	getColorRGB: (r, g, b) -> r * 0x10000 + g * 0x100 + b

	-- Get the red value from an RGB 12 bit integer
	getRed: (color) -> math.floor color / 0x10000

	-- Get the green value from an RGB 12 bit integer
	getGreen: (color) -> math.floor color / 0x100 % 0x100

	-- Get the blue value from an RGB 12 bit integer
	getBlue: (color) -> math.floor color % 0x100

	-- Finds the index of the specified item within a table
	-- Assumes the table is an array indexed table
	indexOf: (tbl, item) -> for i, v in ipairs tbl do return i if item == v

	-- read-only
	DEG: -> DEG

	-- read-only
	NUMBER_MAX_VALUE: -> NUMBER_MAX_VALUE

	-- read-only
	RAD: -> RAD

	-- read-only
	VERSION: -> VERSION

	-- read-only
	BASELAYER: -> BASELAYER

	-- Returns true if the scene doesn't match the internal scene or if the
	-- internal scene is null
	sceneIsNull: => @scene != @__scene or @__scene == nil

	-- Linear interpolation between two values.
	-- @param	a		First value.
	-- @param	b		Second value.
	-- @param	t		Interpolation factor.
	-- @return	When t=0, returns a. When t=1, returns b. When t=0.5, will return halfway between a and b. Etc.
	lerp: (a, b, t = 1) -> a + (b - a) * t

	-- Linear interpolation between two colors.
	-- @param	fromColor		First color.
	-- @param	toColor			Second color.
	-- @param	t				Interpolation value. Clamped to the range [0, 1].
	-- return	RGB component-interpolated color value.
	colorLerp: (fromColor, toColor, t = 1) ->
		if t <= 0
			fromColor
		else if t >= 1
			toColor
		else
			f = LP.convertColor fromColor
			d = LP.convertColor toColor
			d[1] -= f[1]
			d[2] -= f[2]
			d[3] -= f[3]
			f[1] = d[1] * t
			f[2] = d[2] * t
			f[3] = d[3] * t
			LP.getColorRGB unpack f

	next: (current, options, loop) ->
	prev: (current, options, loop) ->

	rand: (amount) -> math.random(amount)
	randomizeSeed: -> math.randomseed os.time()

	resetCamera: -> camera.x, camera.y = 0, 0

	resize: (width, height) ->
		-- resize scene to scale
		width /= LP.screen\fullScaleX!
		height = height / LP.screen\fullScaleY!
		LP.width, LP.height = width, height
		LP.bounds.width, LP.bounds.height = width, height
		LP.screen\resize!

	rotateAround: (object, anchor, angle, relative) ->
	round: (num, precision) ->
	scale: (value, min, max, min2, max2) ->
	scaleClamp: (value, min, max, min2, max2) ->

	-- Sets the camera's x and y
	setCamera: (x, y) -> LP.camera.x, LP.camera.y = x, y
	shuffle: (a) ->

	-- Finds the sign of the provided value.
	-- @param	value		The Float to evaluate.
	-- @return	1 if value > 0, -1 if value < 0, and 0 when value == 0.
	sign: (value) -> if value < 0 then -1 else if value == 0 then 0 else 1

	stepTowards: (object, x, y, distance) ->
	swap: (current, a, b) ->
	swapScene: => @__scene = @scene
	timeFlag: ->
	tween: (object, values, duration, options) ->

	-- Used to determine the Löve version that's being used
	-- @param	version		The string containing the major.minor Löve version
	-- @return	true if version matches the Löve version
	__love: (version) -> version == string.format "%d.%d", love._version_major, love._version_minor

LP = lp!

-- Don't allow unknown Löve versions
if not (LP.__love("0.8") or LP.__love("0.9"))
	assert false, "Unsupported Löve version"

require "LunoPunk.geometry.Point"
require "LunoPunk.geometry.Rectangle"
require "LunoPunk.geometry.Matrix"
require "LunoPunk.Scene"
require "LunoPunk.Tweener"

export ^

class lp
	-- Private
	BASELAYER = 10
	DEG = -180 / math.pi
	INT_MAX_VALUE = nil
	NUMBER_MAX_VALUE = nil
	RAD = math.pi / -180
	VERSION = "0.0.1"
	__scene = nil

	_gameTime: nil
	_renderTime: nil
	_systemTime: nil
	_updateTime: nil
	assignedFrameRate: nil
	blackColor: 0x00000000
	bounds: nil
	buffer: nil
	camera: Point!
	console: nil
	defaultFont: "default"
	elapsed: 0
	engine: nil
	entity: nil
	fixed: false
	focused: nil
	frameRate: 0
	log: nil
	matrix: Matrix!
	orientations: nil
	pan: nil
	point: Point!
	point2: Point!
	random: -> math.random!
	randomSeed: nil
	rate: 0
	rect: Rectangle!
	renderMode = (value) ->
		return __renderMode if value == nil or __renderMode == value
		__renderMode = value

		-- recreate screen for buffer rendering
		if LP.screen == nil
			LP.screen = Screen!
		else
			LP.screen.init!

		value

	scene: Scene!
	screen: nil
	sprite: nil
	tweener: Tweener!
	volume: 1
	watch: nil
	world: nil
	zero: Point!

	-- Window/resolution related fields
	fullscreen: false
	halfHeight: -> LP.height/2
	halfWidth: -> LP.width/2
	height: 0
	width: 0
	windowHeight: 0
	windowWidth: 0

	alarm: (delay, complete, typeweenType, tweener) ->
	anchorTo: (object, anchor, distance) ->
	angle: (x1, y1, x2, y2) ->
	angleDifference: (angle1, angle2) ->
	angleXY: (object, angle, length, x, y) ->

	-- Approaches the value towards the target, by the specified amount, without overshooting the target.
	-- @param	value	The starting value.
	-- @param	target	The target that you want value to approach.
	-- @param	amount	How much you want the value to approach target by.
	-- @return	The new value.
	approach: (value, target, amount) ->
		if value < target - amount
			value + amount
		else if value > target + amount
			value - amount
		else
			target

	choose: (objs) ->
	clamp: (value, min, max) -> if value < min
			min
		else if value > max
			max
		else
			value
	clampInRect: (object, x, y, width, height, padding) ->
	clear: (t) -> while #t > 0 do table.remove t
	consoleEnabled: ->
	createBitmap: (width, height, transparent, color) ->
	distance: (x1, y1, x2, y2) ->
	istanceRectPoint: (px, py, rx, ry, rw, rh) ->
	distanceRects: (x1, y1, w1, h1, x2, y2, w2, h2) ->
	distanceSquared: (x1, y1, xprint2, y2) ->
	frames: (from_frame, to_frame, skip) ->
	getImage: (source) -> love.graphics.newImage source
	getColorHSV: (h, s, v) ->
	getColorHue: (color) ->

	getColorSaturation: (color) ->
	getColorValue: (color) ->

	convertColor: (color) -> { lp.getRed(color), lp.getGreen(color), lp.getBlue(color) }
	getColorRGB: (r, g, b) -> r * 0x10000 + g * 0x100 + b
	getRed: (color) -> math.floor color / 0x10000
	getGreen: (color) -> math.floor color / 0x100 % 0x100
	getBlue: (color) -> math.floor color % 0x100

	indexOf: (tbl, item) -> for i, v in ipairs tbl do return i if item == v

	-- read-only
	DEG: -> DEG

	-- read-only
	NUMBER_MAX_VALUE: -> NUMBER_MAX_VALUE

	-- read-only
	RAD: -> RAD

	-- read-only
	VERSION: -> VERSION

	-- read-only
	BASELAYER: -> BASELAYER

	sceneIsNull: => @scene != @__scene or @__scene == nil

	-- Linear interpolation between two values.
	-- @param	a		First value.
	-- @param	b		Second value.
	-- @param	t		Interpolation factor.
	-- @return	When t=0, returns a. When t=1, returns b. When t=0.5, will return halfway between a and b. Etc.
	lerp: (a, b, t = 1) -> a + (b - a) * t

	-- Linear interpolation between two colors.
	-- @param	fromColor		First color.
	-- @param	toColor			Second color.
	-- @param	t				Interpolation value. Clamped to the range [0, 1].
	-- return	RGB component-interpolated color value.
	colorLerp: (fromColor, toColor, t = 1) ->
		if t <= 0
			fromColor
		else if t >= 1
			toColor
		else
			f = LP.convertColor fromColor
			d = LP.convertColor toColor
			d[1] -= f[1]
			d[2] -= f[2]
			d[3] -= f[3]
			f[1] = d[1] * t
			f[2] = d[2] * t
			f[3] = d[3] * t
			LP.getColorRGB unpack f

	next: (current, options, loop) ->
	prev: (current, options, loop) ->

	rand: (amount) -> math.random(amount)
	randomizeSeed: -> math.randomseed os.time()

	resetCamera: -> camera.x, camera.y = 0, 0

	resize: (width, height) ->
		-- resize scene to scale
		width /= LP.screen\fullScaleX!
		height = height / LP.screen\fullScaleY!
		LP.width, LP.height = width, height
		LP.bounds.width, LP.bounds.height = width, height
		LP.screen\resize!

	rotateAround: (object, anchor, angle, relative) ->
	round: (num, precision) ->
	scale: (value, min, max, min2, max2) ->
	scaleClamp: (value, min, max, min2, max2) ->
	setCamera: (x, y) -> LP.camera.x, LP.camera.y = x, y
	shuffle: (a) ->

	-- Finds the sign of the provided value.
	-- @param	value		The Float to evaluate.
	-- @return	1 if value > 0, -1 if value < 0, and 0 when value == 0.
	sign: (value) -> if value < 0 then -1 else if value == 0 then 0 else 1

	stepTowards: (object, x, y, distance) ->
	swap: (current, a, b) ->
	swapScene: => @__scene = @scene
	timeFlag: ->
	tween: (object, values, duration, options) ->

	-- Used to determine the Löve version that's being used
	__love: (version) -> version == string.format "%d.%d", love._version_major, love._version_minor

LP = lp!

-- Don't allow unknown Löve versions
if not (LP.__love("0.8") or LP.__love("0.9"))
	assert false, "Unsupported Löve version"


nil call

nil call


added is_admin field

added is_admin field

import mixin_table from require "moon"
import create_table, types, add_column from require "lapis.db.schema"

migrations={
  ["custom_user_management_000001"]: ->
    add_column "lazuli_modules_user_management_users", "is_admin", types.boolean default:false
}

mixin_model=(name)-> mixin_table migrations, {"%s_%06d"\format(name,k), v for k,v in pairs (require "models."..name).migrations}

mixin_table migrations, require "lazuli.modules.user_management.migrations"

mixin_model "profiles"
mixin_model "acls"
mixin_model "acl_entries"

migrations

import mixin_table from require "moon"
import create_table, types, add_column from require "lapis.db.schema"

migrations={}

mixin_model=(name)-> mixin_table migrations, {"%s_%06d"\format(name,k), v for k,v in pairs (require "models."..name).migrations}

mixin_table migrations, require "lazuli.modules.user_management.migrations"

mixin_model "profiles"
mixin_model "acls"
mixin_model "acl_entries"

migrations