textutils: add functions to access the util lib

Former-commit-id: 01b008c81bb9a25fa674e167e47d8145be445713 [formerly 5977c348e36eeb4b9de109a793ad2f9fd9598e67]
Former-commit-id: 3e48f42507c8b25113d67cfea1dd78121358d9f8

PRIVMSG:
  '^%pupper (.+)$': (source, destination, arg) =>
    say arg\upper!
  '^%plower (.+)$': (source, destination, arg) =>
    say arg\lower!
  '^%preverse (.+)$': (source, destination, arg) =>
    say arg\reverse!
  '^%plen (.+)$': (source, destination, arg) =>
    say tostring(arg\len!)
  '^%pnicks$': (source, destination) =>
    say table.concat([n for n,k in pairs(ivar2.channels[destination].nicks)], ' ')
  '^%prandom (.+)$': (source, destination, arg) =>
    words = [word for word in arg\gmatch('%S+')]
    say words[math.random(1, #words)]
  '^%pbold (.+)$': (source, destination, arg) =>
    say ivar2.util.bold(arg)
  '^%punderline (.+)$': (source, destination, arg) =>
    say ivar2.util.underline(arg)
  '^%pitalic (.+)$': (source, destination, arg) =>
    say ivar2.util.italic(arg)
  '^%pinvert (.+)$': (source, destination, arg) =>
    say ivar2.util.reverse(arg)
  '^%ptrim (.+)$': (source, destination, arg) =>
    say ivar2.util.trim(arg)
  '^%pcolor ([0-9]+) (.+)$': (source, destination, color, arg) =>
    say ivar2.util.color(arg, color)


remove endorsements graph, doesn't work because of mixed content

class About extends require "views.base"
  content: =>

    h2 class: "list-header", "About Lua Toolbox"
    div class: "cell", ->
        p ->
            text "Lua Toolbox is an "
            a href: "https://github.com/catwell/lua-toolbox/", "Open Source"
            text " website aiming at helping with the discovery and selection"
            text " of Lua modules."
        p ->
            text "For that pupose, it lets its users endorse modules and"
            text " ranks them by number of endorsements. It also has a labels"
            text " system to classify modules by intended use."
        p ->
            text "To help with the project or report bugs, use GitHub issues."
            text " To report security issues that should be fixed before they"
            text " are disclosed, please get in touch with "
            a href:  "http://catwell.info", "Pierre Chapuis"
            text "."

    -- h2 class: "list-header", "Metrics"
    -- div class: "cell", ->
    --     h3 "Endorsements per module"
    --     object type: "image/svg+xml", data: table.concat {
    --         "http://api.chartspree.com/bar.svg",
    --         "?series=#{table.concat(@endorsement_data.values, ",")}",
    --         "&_labels=#{table.concat(@endorsement_data.labels, ",")}",
    --         "&_show_legend=false",
    --         "&_height=300px",
    --     }

class About extends require "views.base"
  content: =>

    h2 class: "list-header", "About Lua Toolbox"
    div class: "cell", ->
        p ->
            text "Lua Toolbox is an "
            a href: "https://github.com/catwell/lua-toolbox/", "Open Source"
            text " website aiming at helping with the discovery and selection"
            text " of Lua modules."
        p ->
            text "For that pupose, it lets its users endorse modules and"
            text " ranks them by number of endorsements. It also has a labels"
            text " system to classify modules by intended use."
        p ->
            text "To help with the project or report bugs, use GitHub issues."
            text " To report security issues that should be fixed before they"
            text " are disclosed, please get in touch with "
            a href:  "http://catwell.info", "Pierre Chapuis"
            text "."

    h2 class: "list-header", "Metrics"
    div class: "cell", ->
        h3 "Endorsements per module"
        object type: "image/svg+xml", data: table.concat {
            "http://api.chartspree.com/bar.svg",
            "?series=#{table.concat(@endorsement_data.values, ",")}",
            "&_labels=#{table.concat(@endorsement_data.labels, ",")}",
            "&_show_legend=false",
            "&_height=300px",
        }


Disallow drive

Disallow drive

--
-- Copyright (C) 2017 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.
--

timer.Create 'PPM2.ModelWatchdog', 1, 0, ->
    for ply in *player.GetAll()
        model = ply\GetModel()
        ply.__ppm2_lastmodel = ply.__ppm2_lastmodel or model
        if ply.__ppm2_lastmodel ~= model
            data = ply\GetPonyData()
            if data and data.ModelChanges
                data\ModelChanges(ply.__ppm2_lastmodel, model)
                ply.__ppm2_lastmodel = model
    for task in *PPM2.NetworkedPonyData.RenderTasks
        ply = task.ent
        if IsValid(ply)
            model = ply\GetModel()
            ply.__ppm2_lastmodel = ply.__ppm2_lastmodel or model
            if ply.__ppm2_lastmodel ~= model
                data = ply\GetPonyData()
                if data and data.ModelChanges
                    data\ModelChanges(ply.__ppm2_lastmodel, model)
                    ply.__ppm2_lastmodel = model

do
    catchError = (err) ->
        PPM2.Message 'Slow Update Error: ', err
        PPM2.Message debug.traceback()
    timer.Create 'PPM2.SlowUpdate', CLIENT and 0.5 or 5, 0, ->
        for ply in *player.GetAll()
            continue if not ply\Alive() or not ply\IsPonyCached() or not ply\GetPonyData()
            data = ply\GetPonyData()
            xpcall(data.SlowUpdate, catchError, data, CLIENT) if data.SlowUpdate
        for task in *PPM2.NetworkedPonyData.RenderTasks
            if IsValid(task.ent) and task.ent\IsPony()
                xpcall(task.SlowUpdate, catchError, task, CLIENT) if task.SlowUpdate

DISABLE_HOOFSTEP_SOUND_CLIENT = CreateConVar('ppm2_cl_no_hoofsound', '0', {FCVAR_ARCHIVE}, 'Disable hoofstep sound play time') if CLIENT
DISABLE_HOOFSTEP_SOUND = CreateConVar('ppm2_no_hoofsound', '0', {FCVAR_ARCHIVE, FCVAR_REPLICATED}, 'Disable hoofstep sound play time')

hook.Add 'PlayerStepSoundTime', 'PPM2.Hooks', (stepType = STEPSOUNDTIME_NORMAL, isWalking = false) =>
    return if not IsValid(@)
    return if not @IsPonyCached()
    return if CLIENT and DISABLE_HOOFSTEP_SOUND_CLIENT\GetBool()
    return if DISABLE_HOOFSTEP_SOUND\GetBool()
    rate = @GetPlaybackRate() * .5
    if @Crouching()
        switch stepType
            when STEPSOUNDTIME_NORMAL
                return not isWalking and (300 / rate) or (600 / rate)
            when STEPSOUNDTIME_ON_LADDER
                return 500 / rate
            when STEPSOUNDTIME_WATER_KNEE
                return not isWalking and (400 / rate) or (800 / rate)
            when STEPSOUNDTIME_WATER_FOOT
                return not isWalking and (350 / rate) or (700 / rate)
    else
        switch stepType
            when STEPSOUNDTIME_NORMAL
                return not isWalking and (150 / rate) or (300 / rate)
            when STEPSOUNDTIME_ON_LADDER
                return 500 / rate
            when STEPSOUNDTIME_WATER_KNEE
                return not isWalking and (250 / rate) or (500 / rate)
            when STEPSOUNDTIME_WATER_FOOT
                return not isWalking and (175 / rate) or (350 / rate)

ENABLE_TOOLGUN = CreateConVar('ppm2_sv_ragdoll_toolgun', '0', {FCVAR_ARCHIVE, FCVAR_REPLICATED, FCVAR_NOTIFY}, 'Allow toolgun usage on player death ragdolls')
ENABLE_PHYSGUN = CreateConVar('ppm2_sv_ragdoll_physgun', '1', {FCVAR_ARCHIVE, FCVAR_REPLICATED, FCVAR_NOTIFY}, 'Allow physgun usage on player death ragdolls')

hook.Add 'CanTool', 'PPM2.DeathRagdoll', (ply = NULL, tr = {Entity: NULL}, tool = '') -> false if IsValid(tr.Entity) and tr.Entity\GetNWBool('PPM2.IsDeathRagdoll') and not ENABLE_TOOLGUN\GetBool()
hook.Add 'PhysgunPickup', 'PPM2.DeathRagdoll', (ply = NULL, ent = NULL) -> false if IsValid(ent) and ent\GetNWBool('PPM2.IsDeathRagdoll') and not ENABLE_PHYSGUN\GetBool()
hook.Add 'CanProperty', 'PPM2.DeathRagdoll', (ply = NULL, mode = '', ent = NULL) -> false if IsValid(ent) and ent\GetNWBool('PPM2.IsDeathRagdoll') and not ENABLE_TOOLGUN\GetBool()
hook.Add 'CanDrive', 'PPM2.DeathRagdoll', (ply = NULL, ent = NULL) -> false if IsValid(ent) and ent\GetNWBool('PPM2.IsDeathRagdoll') and not ENABLE_TOOLGUN\GetBool()


Don't display loading on dead players

Don't display loading on dead players

--
-- Copyright (C) 2017-2019 DBot

-- 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.

import FrameNumberL, RealTimeL, PPM2 from _G
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')
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')
PPM2.ENABLE_NEW_RAGDOLLS = CreateConVar('ppm2_sv_new_ragdolls', '1', {FCVAR_NOTIFY, FCVAR_REPLICATED}, 'Enable new ragdolls')
ENABLE_NEW_RAGDOLLS = PPM2.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')

VM_MAGIC = CreateConVar('ppm2_cl_vm_magic', '0', {FCVAR_ARCHIVE, FCVAR_USERINFO}, 'Modify viewmodel when pony has horn for more immersion. Has no effect when ppm2_cl_vm_magic_hands is on')
VM_MAGIC_HANDS = CreateConVar('ppm2_cl_vm_magic_hands', '1', {FCVAR_ARCHIVE, FCVAR_NOTIFY, FCVAR_USERINFO}, 'Use magic hands when pony has horn. Due to gmod behavior, sometimes this does not work')

PPM2.VM_MAGIC = VM_MAGIC
PPM2.VM_MAGIC_HANDS = VM_MAGIC_HANDS

validHands = (hands) -> hands == 'models/ppm/c_arms_pony.mdl'

hook.Add 'PreDrawPlayerHands', 'PPM2.ViewModel', (arms = NULL, viewmodel = NULL, ply = LocalPlayer(), weapon = NULL) ->
	return if PPM2.__RENDERING_REFLECTIONS or not IsValid(arms) or not ply.__cachedIsPony
	observer = ply\GetObserverTarget()
	return if IsValid(observer) and not observer.__cachedIsPony
	return true if not SV_SHOULD_DRAW_VIEWMODEL\GetBool() or not SHOULD_DRAW_VIEWMODEL\GetBool()

	if IsValid(observer)
		return if not observer\Alive()
	else
		return if not ply\Alive()

	arms\SetPos(ply\EyePos() + Vector(0, 0, 100))
	wep = ply\GetActiveWeapon()

	return true if IsValid(wep) and wep.UseHands == false
	amodel = arms\GetModel()
	return if not validHands(amodel)

	data = ply\GetPonyData()
	return unless data

	return true if data\GetPonyRaceFlags()\band(PPM2.RACE_HAS_HORN) ~= 0 and VM_MAGIC\GetBool() and not VM_MAGIC_HANDS\GetBool()

	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

local lastPos, lastAng

CalcViewModelView = (weapon, vm, oldPos, oldAng, pos, ang) ->
	return if not VM_MAGIC\GetBool() or VM_MAGIC_HANDS\GetBool()
	ply = LocalPlayer()
	return if PPM2.__RENDERING_REFLECTIONS or not IsValid(vm) or not ply.__cachedIsPony
	observer = ply\GetObserverTarget()
	return if IsValid(observer) and not observer.__cachedIsPony
	return if not SV_SHOULD_DRAW_VIEWMODEL\GetBool() or not SHOULD_DRAW_VIEWMODEL\GetBool()

	if IsValid(observer)
		return if not observer\Alive()
	else
		return if not ply\Alive()

	return if ply\GetPonyRaceFlags()\band(PPM2.RACE_HAS_HORN) == 0
	return if IsValid(weapon) and weapon.UseHands == false
	shouldShift = not (weapon.IsTFA and weapon\IsTFA() and weapon.IronSightsProgress > 0.6)
	-- since pos and ang never get refreshed, i would modify those to get desired results
	-- with other addons installed

	if shouldShift
		fwd = ply\EyeAngles()\Forward()
		right = ply\EyeAngles()\Right()
		pos2 = pos + fwd * math.sin(CurTimeL()) + right * math.cos(CurTimeL() + 1.2) + right * 4 - fwd * 2
		ang2 = Angle(ang)
		pos.z -= 4
		ang2\RotateAroundAxis(ang\Forward(), -30)
		lastPos = pos2
		lastAng = ang2
	else
		lastPos = LerpVector(FrameTime() * 11, lastPos or pos, pos)
		lastAng = LerpAngle(FrameTime() * 11, lastAng or ang, ang)

	pos.x, pos.y, pos.z = lastPos.x, lastPos.y, lastPos.z
	ang.p, ang.y, ang.r = lastAng.p, lastAng.y, lastAng.r

hook.Add 'CalcViewModelView', 'PPM2.ViewModel', CalcViewModelView, -3

-- indraw = false
--
-- magicMat = CreateMaterial('ppm2_magic_material', 'UnlitGeneric', {
--  '$basetexture': 'models/debug/debugwhite'
--  '$ignorez': 1
--  '$vertexcolor': 1
--  '$vertexalpha': 1
--  '$nolod': 1
--  '$color2': '{255 255 255}'
-- })

-- hook.Add 'PostDrawViewModel', 'PPM2.ViewModel', (vm, ply, weapon) ->
--  return if indraw
--  return if not VM_MAGIC\GetBool() or VM_MAGIC_HANDS\GetBool()
--  return if not IsValid(vm) or not IsValid(ply) or not IsValid(weapon)
--  return if PPM2.__RENDERING_REFLECTIONS or not IsValid(vm) or not ply.__cachedIsPony
--  observer = ply\GetObserverTarget()
--  return if IsValid(observer) and not observer.__cachedIsPony
--  return if not SV_SHOULD_DRAW_VIEWMODEL\GetBool() or not SHOULD_DRAW_VIEWMODEL\GetBool()
--  return if IsValid(weapon) and weapon.UseHands == false
--
--  if IsValid(observer)
--      return if not observer\Alive()
--  else
--      return if not ply\Alive()
--
--  return if ply\GetPonyRaceFlags()\band(PPM2.RACE_HAS_HORN) == 0
--  data = ply\GetPonyData()
--  return if not data
--
--  indraw = true
--  color = data\ComputeMagicColor()
--
--  magicMat\SetVector('$color2', color\ToVector())
--  render.MaterialOverride(magicMat)
--  render.ModelMaterialOverride(magicMat)
--
--  cam.IgnoreZ(true)
--
--  mat = Matrix()
--  mat\Scale(Vector(1.1, 1.1, 1.1))
--  vm\EnableMatrix('RenderMultiply', mat)
--
--  ply\DrawModel()
--  vm\DisableMatrix('RenderMultiply')
--
--  cam.IgnoreZ(false)
--
--  render.MaterialOverride()
--  render.ModelMaterialOverride()
--  indraw = false

mat_dxlevel = GetConVar('mat_dxlevel')

timer.Create 'PPM2.CheckDXLevel', 180, 0, ->
	if mat_dxlevel\GetInt() > 90
		timer.Remove 'PPM2.CheckDXLevel'
		return

	PPM2.Message('Direct3D Level is LESS THAN 9.1! This will not work!')

IN_DRAW = false
MARKED_FOR_DRAW = {}

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

	MARKED_FOR_DRAW = {}

	for _, ply in ipairs 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 bDrawingDepth and not bDrawingSkybox
		for _, draw in ipairs 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 ENABLE_NEW_RAGDOLLS\GetBool()
		for _, ply in ipairs 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 = ->
	for _, task in ipairs 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
				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

	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()
			bones\ResetBones()
			hook.Call('PPM2.SetupBones', nil, @, data) if data
			bones\Think()
			@_ppmBonesModified = true

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

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 = 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 @GetPonyRaceFlags()\band(PPM2.RACE_HAS_HORN) == 0
		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\ComputeMagicColor()
				.haloSeed = math.rad(math.random(-1000, 1000))
		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)

import ScrWL, ScrHL from _G
import HUDCommons from DLib
color_black = Color(color_black)

hook.Add 'HUDPaint', 'PPM2.LoadingDisplay', ->
	lply = LocalPlayer()
	lpos = EyePos()

	for task in *PPM2.NetworkedPonyData.CheckTasks
		if ent = task\GetEntity()
			if IsValid(ent) and ent\IsPony() and not ent\IsRagdoll() and (ent ~= lply or lply\ShouldDrawLocalPlayer()) and (not ent.Alive or ent\Alive())
				if renderer = task\GetRenderController()
					if textures = renderer\GetTextureController()
						if textures\IsBeingProcessed()
							pos = ent\GetPos()
							pos\Add(ent\OBBCenter())
							dist = pos\Distance(lpos)

							if dist < 384
								{:x, :y, :visible} = pos\ToScreen()

								if visible and x > -100 and x < ScrWL() + 100 and y > -100 and y < ScrHL() + 100
									color = task\ComputeMagicColor()
									color.a = 255 - dist\progression(128, 384) * 255
									color_black.a = color.a
									size = (200 * (1 - dist\progression(0, 300))) * task\GetPonySize() * task\GetLegsSize() * task\GetNeckSize() * task\GetBackSize()
									sizeh = size / 2

									HUDCommons.DrawLoading(x + 2 - sizeh, y + 2 - sizeh, size, color_black)
									HUDCommons.DrawLoading(x - sizeh, y - sizeh, size, color)

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-2019 DBot

-- 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.

import FrameNumberL, RealTimeL, PPM2 from _G
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')
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')
PPM2.ENABLE_NEW_RAGDOLLS = CreateConVar('ppm2_sv_new_ragdolls', '1', {FCVAR_NOTIFY, FCVAR_REPLICATED}, 'Enable new ragdolls')
ENABLE_NEW_RAGDOLLS = PPM2.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')

VM_MAGIC = CreateConVar('ppm2_cl_vm_magic', '0', {FCVAR_ARCHIVE, FCVAR_USERINFO}, 'Modify viewmodel when pony has horn for more immersion. Has no effect when ppm2_cl_vm_magic_hands is on')
VM_MAGIC_HANDS = CreateConVar('ppm2_cl_vm_magic_hands', '1', {FCVAR_ARCHIVE, FCVAR_NOTIFY, FCVAR_USERINFO}, 'Use magic hands when pony has horn. Due to gmod behavior, sometimes this does not work')

PPM2.VM_MAGIC = VM_MAGIC
PPM2.VM_MAGIC_HANDS = VM_MAGIC_HANDS

validHands = (hands) -> hands == 'models/ppm/c_arms_pony.mdl'

hook.Add 'PreDrawPlayerHands', 'PPM2.ViewModel', (arms = NULL, viewmodel = NULL, ply = LocalPlayer(), weapon = NULL) ->
	return if PPM2.__RENDERING_REFLECTIONS or not IsValid(arms) or not ply.__cachedIsPony
	observer = ply\GetObserverTarget()
	return if IsValid(observer) and not observer.__cachedIsPony
	return true if not SV_SHOULD_DRAW_VIEWMODEL\GetBool() or not SHOULD_DRAW_VIEWMODEL\GetBool()

	if IsValid(observer)
		return if not observer\Alive()
	else
		return if not ply\Alive()

	arms\SetPos(ply\EyePos() + Vector(0, 0, 100))
	wep = ply\GetActiveWeapon()

	return true if IsValid(wep) and wep.UseHands == false
	amodel = arms\GetModel()
	return if not validHands(amodel)

	data = ply\GetPonyData()
	return unless data

	return true if data\GetPonyRaceFlags()\band(PPM2.RACE_HAS_HORN) ~= 0 and VM_MAGIC\GetBool() and not VM_MAGIC_HANDS\GetBool()

	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

local lastPos, lastAng

CalcViewModelView = (weapon, vm, oldPos, oldAng, pos, ang) ->
	return if not VM_MAGIC\GetBool() or VM_MAGIC_HANDS\GetBool()
	ply = LocalPlayer()
	return if PPM2.__RENDERING_REFLECTIONS or not IsValid(vm) or not ply.__cachedIsPony
	observer = ply\GetObserverTarget()
	return if IsValid(observer) and not observer.__cachedIsPony
	return if not SV_SHOULD_DRAW_VIEWMODEL\GetBool() or not SHOULD_DRAW_VIEWMODEL\GetBool()

	if IsValid(observer)
		return if not observer\Alive()
	else
		return if not ply\Alive()

	return if ply\GetPonyRaceFlags()\band(PPM2.RACE_HAS_HORN) == 0
	return if IsValid(weapon) and weapon.UseHands == false
	shouldShift = not (weapon.IsTFA and weapon\IsTFA() and weapon.IronSightsProgress > 0.6)
	-- since pos and ang never get refreshed, i would modify those to get desired results
	-- with other addons installed

	if shouldShift
		fwd = ply\EyeAngles()\Forward()
		right = ply\EyeAngles()\Right()
		pos2 = pos + fwd * math.sin(CurTimeL()) + right * math.cos(CurTimeL() + 1.2) + right * 4 - fwd * 2
		ang2 = Angle(ang)
		pos.z -= 4
		ang2\RotateAroundAxis(ang\Forward(), -30)
		lastPos = pos2
		lastAng = ang2
	else
		lastPos = LerpVector(FrameTime() * 11, lastPos or pos, pos)
		lastAng = LerpAngle(FrameTime() * 11, lastAng or ang, ang)

	pos.x, pos.y, pos.z = lastPos.x, lastPos.y, lastPos.z
	ang.p, ang.y, ang.r = lastAng.p, lastAng.y, lastAng.r

hook.Add 'CalcViewModelView', 'PPM2.ViewModel', CalcViewModelView, -3

-- indraw = false
--
-- magicMat = CreateMaterial('ppm2_magic_material', 'UnlitGeneric', {
--  '$basetexture': 'models/debug/debugwhite'
--  '$ignorez': 1
--  '$vertexcolor': 1
--  '$vertexalpha': 1
--  '$nolod': 1
--  '$color2': '{255 255 255}'
-- })

-- hook.Add 'PostDrawViewModel', 'PPM2.ViewModel', (vm, ply, weapon) ->
--  return if indraw
--  return if not VM_MAGIC\GetBool() or VM_MAGIC_HANDS\GetBool()
--  return if not IsValid(vm) or not IsValid(ply) or not IsValid(weapon)
--  return if PPM2.__RENDERING_REFLECTIONS or not IsValid(vm) or not ply.__cachedIsPony
--  observer = ply\GetObserverTarget()
--  return if IsValid(observer) and not observer.__cachedIsPony
--  return if not SV_SHOULD_DRAW_VIEWMODEL\GetBool() or not SHOULD_DRAW_VIEWMODEL\GetBool()
--  return if IsValid(weapon) and weapon.UseHands == false
--
--  if IsValid(observer)
--      return if not observer\Alive()
--  else
--      return if not ply\Alive()
--
--  return if ply\GetPonyRaceFlags()\band(PPM2.RACE_HAS_HORN) == 0
--  data = ply\GetPonyData()
--  return if not data
--
--  indraw = true
--  color = data\ComputeMagicColor()
--
--  magicMat\SetVector('$color2', color\ToVector())
--  render.MaterialOverride(magicMat)
--  render.ModelMaterialOverride(magicMat)
--
--  cam.IgnoreZ(true)
--
--  mat = Matrix()
--  mat\Scale(Vector(1.1, 1.1, 1.1))
--  vm\EnableMatrix('RenderMultiply', mat)
--
--  ply\DrawModel()
--  vm\DisableMatrix('RenderMultiply')
--
--  cam.IgnoreZ(false)
--
--  render.MaterialOverride()
--  render.ModelMaterialOverride()
--  indraw = false

mat_dxlevel = GetConVar('mat_dxlevel')

timer.Create 'PPM2.CheckDXLevel', 180, 0, ->
	if mat_dxlevel\GetInt() > 90
		timer.Remove 'PPM2.CheckDXLevel'
		return

	PPM2.Message('Direct3D Level is LESS THAN 9.1! This will not work!')

IN_DRAW = false
MARKED_FOR_DRAW = {}

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

	MARKED_FOR_DRAW = {}

	for _, ply in ipairs 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 bDrawingDepth and not bDrawingSkybox
		for _, draw in ipairs 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 ENABLE_NEW_RAGDOLLS\GetBool()
		for _, ply in ipairs 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 = ->
	for _, task in ipairs 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
				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

	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()
			bones\ResetBones()
			hook.Call('PPM2.SetupBones', nil, @, data) if data
			bones\Think()
			@_ppmBonesModified = true

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

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 = 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 @GetPonyRaceFlags()\band(PPM2.RACE_HAS_HORN) == 0
		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\ComputeMagicColor()
				.haloSeed = math.rad(math.random(-1000, 1000))
		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)

import ScrWL, ScrHL from _G
import HUDCommons from DLib
color_black = Color(color_black)

hook.Add 'HUDPaint', 'PPM2.LoadingDisplay', ->
	lply = LocalPlayer()
	lpos = EyePos()

	for task in *PPM2.NetworkedPonyData.CheckTasks
		if ent = task\GetEntity()
			if IsValid(ent) and ent\IsPony() and not ent\IsRagdoll() and (ent ~= lply or lply\ShouldDrawLocalPlayer())
				if renderer = task\GetRenderController()
					if textures = renderer\GetTextureController()
						if textures\IsBeingProcessed()
							pos = ent\GetPos()
							pos\Add(ent\OBBCenter())
							dist = pos\Distance(lpos)

							if dist < 384
								{:x, :y, :visible} = pos\ToScreen()

								if visible and x > -100 and x < ScrWL() + 100 and y > -100 and y < ScrHL() + 100
									color = task\ComputeMagicColor()
									color.a = 255 - dist\progression(128, 384) * 255
									color_black.a = color.a
									size = (200 * (1 - dist\progression(0, 300))) * task\GetPonySize() * task\GetLegsSize() * task\GetNeckSize() * task\GetBackSize()
									sizeh = size / 2

									HUDCommons.DrawLoading(x + 2 - sizeh, y + 2 - sizeh, size, color_black)
									HUDCommons.DrawLoading(x - sizeh, y - sizeh, size, color)

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


Checks

Checks

--
-- Copyright (C) 2017 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.
--

wUInt = (def = 0, size = 8) ->
    return (arg = def) -> net.WriteUInt(arg, size)

wInt = (def = 0, size = 8) ->
    return (arg = def) -> net.WriteInt(arg, size)

rUInt = (size = 8, min = 0, max = 255) ->
    return -> math.Clamp(net.ReadUInt(size), min, max)

rInt = (size = 8, min = -128, max = 127) ->
    return -> math.Clamp(net.ReadInt(size), min, max)

rFloat = (min = 0, max = 255) ->
    return -> math.Clamp(net.ReadFloat(), min, max)

wFloat = net.WriteFloat
rSEnt = net.ReadStrongEntity
wSEnt = net.WriteStrongEntity
rBool = net.ReadBool
wBool = net.WriteBool
rColor = net.ReadColor
wColor = net.WriteColor
rString = net.ReadString
wString = net.WriteString

COLOR_FIXER = (r = 255, g = 255, b = 255, a = 255) ->
    func = (arg = Color(r, g, b, a)) ->
        if type(arg) ~= 'table'
            return Color(255, 255, 255)
        else
            {:r, :g, :b, :a} = arg
            if r and g and b and a
                return Color(r, g, b, a)
            else
                return Color(255, 255, 255)
    return func

URL_FIXER = (arg = '') ->
    arg = tostring(arg)
    if arg\find('^https?://')
        return arg
    else
        return ''

rURL = -> URL_FIXER(rString())

FLOAT_FIXER = (def = 1, min = 0, max = 1) ->
    defFunc = -> def if type(def) ~= 'function'
    defFunc = def if type(def) == 'function'
    return (arg = defFunc()) -> math.Clamp(tonumber(arg) or defFunc(), min, max)

INT_FIXER = (def = 1, min = 0, max = 1) ->
    defFunc = -> def if type(def) ~= 'function'
    defFunc = def if type(def) == 'function'
    return (arg = defFunc()) -> math.floor(math.Clamp(tonumber(arg) or defFunc(), min, max))

PPM2.PonyDataRegistry = {
    'age': {
        default: -> PPM2.AGE_ADULT
        getFunc: 'Age'
        enum: {'FILLY', 'ADULT', 'MATURE'}
    }

    'race': {
        default: -> PPM2.RACE_EARTH
        getFunc: 'Race'
        enum: [arg for arg in *PPM2.RACE_ENUMS]
    }

    'wings_type': {
        default: -> 0
        getFunc: 'WingsType'
        enum: [arg for arg in *PPM2.AvaliablePonyWings]
    }

    'gender': {
        default: -> PPM2.GENDER_FEMALE
        getFunc: 'Gender'
        enum: [arg for arg in *PPM2.AGE_ENUMS]
    }

    'weight': {
        default: -> 1
        getFunc: 'Weight'
        min: PPM2.MIN_WEIGHT
        max: PPM2.MAX_WEIGHT
        type: 'FLOAT'
    }

    'ponysize': {
        default: -> 1
        getFunc: 'PonySize'
        min: PPM2.MIN_SCALE
        max: PPM2.MAX_SCALE
        type: 'FLOAT'
    }

    'necksize': {
        default: -> 1
        getFunc: 'NeckSize'
        min: PPM2.MIN_NECK
        max: PPM2.MAX_NECK
        type: 'FLOAT'
    }

    'legssize': {
        default: -> 1
        getFunc: 'LegsSize'
        min: PPM2.MIN_LEGS
        max: PPM2.MAX_LEGS
        type: 'FLOAT'
    }

    'male_buff': {
        default: -> PPM2.DEFAULT_MALE_BUFF
        getFunc: 'MaleBuff'
        min: PPM2.MIN_MALE_BUFF
        max: PPM2.MAX_MALE_BUFF
        type: 'FLOAT'
    }

    'eyelash': {
        default: -> 0
        getFunc: 'EyelashType'
        enum: [arg for arg in *PPM2.EyelashTypes]
    }

    'tail': {
        default: -> 0
        getFunc: 'TailType'
        enum: [arg for arg in *PPM2.AvaliableTails]
    }

    'tail_new': {
        default: -> 0
        getFunc: 'TailTypeNew'
        enum: [arg for arg in *PPM2.AvaliableTailsNew]
    }

    'mane': {
        default: -> 0
        getFunc: 'ManeType'
        enum: [arg for arg in *PPM2.AvaliableUpperManes]
    }

    'mane_new': {
        default: -> 0
        getFunc: 'ManeTypeNew'
        enum: [arg for arg in *PPM2.AvaliableUpperManesNew]
    }

    'manelower': {
        default: -> 0
        getFunc: 'ManeTypeLower'
        enum: [arg for arg in *PPM2.AvaliableLowerManes]
    }

    'manelower_new': {
        default: -> 0
        getFunc: 'ManeTypeLowerNew'
        enum: [arg for arg in *PPM2.AvaliableLowerManesNew]
    }

    'socks_texture': {
        default: -> 0
        getFunc: 'SocksTexture'
        enum: [arg for arg in *PPM2.SocksTypes]
    }

    'socks_texture_url': {
        default: -> ''
        getFunc: 'SocksTextureURL'
        type: 'URL'
    }

    'tailsize': {
        default: -> 1
        getFunc: 'TailSize'
        min: PPM2.MIN_TAIL_SIZE
        max: PPM2.MAX_TAIL_SIZE
        type: 'FLOAT'
    }

    'cmark': {
        default: -> true
        getFunc: 'CMark'
        type: 'BOOLEAN'
    }

    'cmark_size': {
        default: -> 1
        getFunc: 'CMarkSize'
        min: 0
        max: 1
        type: 'FLOAT'
    }

    'cmark_color': {
        default: -> Color(255, 255, 255)
        getFunc: 'CMarkColor'
        type: 'COLOR'
    }

    'fangs': {
        default: -> false
        getFunc: 'Fangs'
        type: 'BOOLEAN'
    }

    'bat_pony_ears': {
        default: -> false
        getFunc: 'BatPonyEars'
        type: 'BOOLEAN'
    }

    'claw_teeth': {
        default: -> false
        getFunc: 'ClawTeeth'
        type: 'BOOLEAN'
    }

    'cmark_type': {
        default: -> 4
        getFunc: 'CMarkType'
        enum: [arg for arg in *PPM2.DefaultCutiemarks]
    }

    'cmark_url': {
        default: -> ''
        getFunc: 'CMarkURL'
        type: 'URL'
    }

    'body': {
        default: -> Color(255, 255, 255)
        getFunc: 'BodyColor'
        type: 'COLOR'
    }

    'horn_color': {
        default: -> Color(255, 255, 255)
        getFunc: 'HornColor'
        type: 'COLOR'
    }

    'wings_color': {
        default: -> Color(255, 255, 255)
        getFunc: 'WingsColor'
        type: 'COLOR'
    }

    'separate_wings': {
        default: -> false
        getFunc: 'SeparateWings'
        type: 'BOOLEAN'
    }

    'separate_horn': {
        default: -> false
        getFunc: 'SeparateHorn'
        type: 'BOOLEAN'
    }

    'use_horn_detail': {
        default: -> false
        getFunc: 'UseHornDetail'
        type: 'BOOLEAN'
    }

    'horn_glow': {
        default: -> false
        getFunc: 'HornGlow'
        type: 'BOOLEAN'
    }

    'horn_glow_strength': {
        default: -> 1
        getFunc: 'HornGlowSrength'
        min: 0
        max: 1
        type: 'FLOAT'
    }

    'horn_detail_color': {
        default: -> Color(255, 255, 255)
        getFunc: 'HornDetailColor'
        type: 'COLOR'
    }

    'separate_eyes': {
        default: -> false
        getFunc: 'SeparateEyes'
        type: 'BOOLEAN'
    }

    'separate_mane': {
        default: -> false
        getFunc: 'SeparateMane'
        type: 'BOOLEAN'
    }

    'socks': {
        default: -> false
        getFunc: 'Socks'
        type: 'BOOLEAN'
    }

    'new_male_muzzle': {
        default: -> true
        getFunc: 'NewMuzzle'
        type: 'BOOLEAN'
    }

    'noflex': {
        default: -> false
        getFunc: 'NoFlex'
        type: 'BOOLEAN'
    }

    'socks_model': {
        default: -> false
        getFunc: 'SocksAsModel'
        type: 'BOOLEAN'
    }

    'socks_model_color': {
        default: -> Color(255, 255, 255)
        getFunc: 'SocksColor'
        type: 'COLOR'
    }

    'suit': {
        default: -> 0
        getFunc: 'Bodysuit'
        enum: [arg for arg in *PPM2.AvaliablePonySuits]
    }

    'left_wing_size': {
        default: -> 1
        getFunc: 'LWingSize'
        min: PPM2.MIN_WING
        max: PPM2.MAX_WING
        type: 'FLOAT'
    }

    'left_wing_x': {
        default: -> 0
        getFunc: 'LWingX'
        min: PPM2.MIN_WINGX
        max: PPM2.MAX_WINGX
        type: 'FLOAT'
    }

    'left_wing_y': {
        default: -> 0
        getFunc: 'LWingY'
        min: PPM2.MIN_WINGY
        max: PPM2.MAX_WINGY
        type: 'FLOAT'
    }

    'left_wing_z': {
        default: -> 0
        getFunc: 'LWingZ'
        min: PPM2.MIN_WINGZ
        max: PPM2.MAX_WINGZ
        type: 'FLOAT'
    }

    'right_wing_size': {
        default: -> 1
        getFunc: 'RWingSize'
        min: PPM2.MIN_WING
        max: PPM2.MAX_WING
        type: 'FLOAT'
    }

    'right_wing_x': {
        default: -> 0
        getFunc: 'RWingX'
        min: PPM2.MIN_WINGX
        max: PPM2.MAX_WINGX
        type: 'FLOAT'
    }

    'right_wing_y': {
        default: -> 0
        getFunc: 'RWingY'
        min: PPM2.MIN_WINGY
        max: PPM2.MAX_WINGY
        type: 'FLOAT'
    }

    'right_wing_z': {
        default: -> 0
        getFunc: 'RWingZ'
        min: PPM2.MIN_WINGZ
        max: PPM2.MAX_WINGZ
        type: 'FLOAT'
    }

    'teeth_color': {
        default: -> Color(255, 255, 255)
        getFunc: 'TeethColor'
        type: 'COLOR'
    }

    'mouth_color': {
        default: -> Color(219, 65, 155)
        getFunc: 'MouthColor'
        type: 'COLOR'
    }

    'tongue_color': {
        default: -> Color(235, 131, 59)
        getFunc: 'TongueColor'
        type: 'COLOR'
    }

    'bat_wing_color': {
        default: -> Color(255, 255, 255)
        getFunc: 'BatWingColor'
        type: 'COLOR'
    }

    'bat_wing_skin_color': {
        default: -> Color(255, 255, 255)
        getFunc: 'BatWingSkinColor'
        type: 'COLOR'
    }

    'separate_horn_phong': {
        default: -> false
        getFunc: 'SeparateHornPhong'
        type: 'BOOLEAN'
    }

    'separate_wings_phong': {
        default: -> false
        getFunc: 'SeparateWingsPhong'
        type: 'BOOLEAN'
    }

    'separate_mane_phong': {
        default: -> false
        getFunc: 'SeparateManePhong'
        type: 'BOOLEAN'
    }

    'separate_tail_phong': {
        default: -> false
        getFunc: 'SeparateTailPhong'
        type: 'BOOLEAN'
    }

    'alternative_fangs': {
        default: -> false
        getFunc: 'AlternativeFangs'
        type: 'BOOLEAN'
    }

    'hoof_fluffers': {
        default: -> false
        getFunc: 'HoofFluffers'
        type: 'BOOLEAN'
    }

    'hoof_fluffers_strength': {
        default: -> 1
        getFunc: 'HoofFluffersStrength'
        min: 0
        max: 1
        type: 'FLOAT'
    }

    'ears_size': {
        default: -> 1
        getFunc: 'EarsSize'
        min: 0.1
        max: 2
        type: 'FLOAT'
    }

    'bat_pony_ears_strength': {
        default: -> 1
        getFunc: 'BatPonyEarsStrength'
        min: 0
        max: 1
        type: 'FLOAT'
    }

    'fangs_strength': {
        default: -> 1
        getFunc: 'FangsStrength'
        min: 0
        max: 1
        type: 'FLOAT'
    }

    'clawteeth_strength': {
        default: -> 1
        getFunc: 'ClawTeethStrength'
        min: 0
        max: 1
        type: 'FLOAT'
    }
}

for {internal, publicName} in *{{'_left', 'Left'}, {'_right', 'Right'}, {'', ''}}
    PPM2.PonyDataRegistry["eye_url#{internal}"] = {
        default: -> ''
        getFunc: "EyeURL#{publicName}"
        type: 'URL'
    }

    PPM2.PonyDataRegistry["eye_bg#{internal}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "EyeBackground#{publicName}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["eye_hole#{internal}"] = {
        default: -> Color(0, 0, 0)
        getFunc: "EyeHole#{publicName}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["eye_iris1#{internal}"] = {
        default: -> Color(200, 200, 200)
        getFunc: "EyeIrisTop#{publicName}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["eye_iris2#{internal}"] = {
        default: -> Color(200, 200, 200)
        getFunc: "EyeIrisBottom#{publicName}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["eye_irisline1#{internal}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "EyeIrisLine1#{publicName}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["eye_irisline2#{internal}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "EyeIrisLine2#{publicName}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["eye_reflection#{internal}"] = {
        default: -> Color(255, 255, 255, 127)
        getFunc: "EyeReflection#{publicName}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["eye_effect#{internal}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "EyeEffect#{publicName}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["eye_lines#{internal}"] = {
        default: -> true
        getFunc: "EyeLines#{publicName}"
        type: 'BOOLEAN'
    }

    PPM2.PonyDataRegistry["eye_iris_size#{internal}"] = {
        default: -> 1
        getFunc: "IrisSize#{publicName}"
        min: PPM2.MIN_IRIS
        max: PPM2.MAX_IRIS
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["eye_derp#{internal}"] = {
        default: -> false
        getFunc: "DerpEyes#{publicName}"
        type: 'BOOLEAN'
    }

    PPM2.PonyDataRegistry["eye_derp_strength#{internal}"] = {
        default: -> 1
        getFunc: "DerpEyesStrength#{publicName}"
        min: PPM2.MIN_DERP_STRENGTH
        max: PPM2.MAX_DERP_STRENGTH
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["eye_type#{internal}"] = {
        default: -> 0
        getFunc: "EyeType#{publicName}"
        enum: [arg for arg in *PPM2.AvaliableEyeTypes]
    }

    PPM2.PonyDataRegistry["hole_width#{internal}"] = {
        default: -> 1
        getFunc: "HoleWidth#{publicName}"
        min: PPM2.MIN_PUPIL_SIZE
        max: PPM2.MAX_PUPIL_SIZE
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["hole_height#{internal}"] = {
        default: -> 1
        getFunc: "HoleHeight#{publicName}"
        min: PPM2.MIN_PUPIL_SIZE
        max: PPM2.MAX_PUPIL_SIZE
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["iris_width#{internal}"] = {
        default: -> 1
        getFunc: "IrisWidth#{publicName}"
        min: PPM2.MIN_PUPIL_SIZE
        max: PPM2.MAX_PUPIL_SIZE
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["iris_height#{internal}"] = {
        default: -> 1
        getFunc: "IrisHeight#{publicName}"
        min: PPM2.MIN_PUPIL_SIZE
        max: PPM2.MAX_PUPIL_SIZE
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["hole_shiftx#{internal}"] = {
        default: -> 0
        getFunc: "HoleShiftX#{publicName}"
        min: PPM2.MIN_HOLE_SHIFT
        max: PPM2.MAX_HOLE_SHIFT
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["hole_shifty#{internal}"] = {
        default: -> 0
        getFunc: "HoleShiftY#{publicName}"
        min: PPM2.MIN_HOLE_SHIFT
        max: PPM2.MAX_HOLE_SHIFT
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["eye_hole_size#{internal}"] = {
        default: -> .8
        getFunc: "HoleSize#{publicName}"
        min: PPM2.MIN_HOLE
        max: PPM2.MAX_HOLE
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["eye_rotation#{internal}"] = {
        default: -> 0
        getFunc: "EyeRotation#{publicName}"
        min: PPM2.MIN_EYE_ROTATION
        max: PPM2.MAX_EYE_ROTATION
        type: 'INT'
    }

for i = 1, 3
    PPM2.PonyDataRegistry["horn_url_#{i}"] = {
        default: -> ''
        getFunc: "HornURL#{i}"
        type: 'URL'
    }

    PPM2.PonyDataRegistry["bat_wing_url_#{i}"] = {
        default: -> ''
        getFunc: "BatWingURL#{i}"
        type: 'URL'
    }

    PPM2.PonyDataRegistry["bat_wing_skin_url_#{i}"] = {
        default: -> ''
        getFunc: "BatWingSkinURL#{i}"
        type: 'URL'
    }

    PPM2.PonyDataRegistry["wings_url_#{i}"] = {
        default: -> ''
        getFunc: "WingsURL#{i}"
        type: 'URL'
    }

    PPM2.PonyDataRegistry["horn_url_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "HornURLColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["bat_wing_url_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "BatWingURLColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["bat_wing_skin_url_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "BatWingSkinURLColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["wings_url_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "WingsURLColor#{i}"
        type: 'COLOR'
    }

for i = 1, 6
    PPM2.PonyDataRegistry["socks_detail_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "SocksDetailColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["mane_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "ManeColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["mane_detail_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "ManeDetailColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["mane_url_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "ManeURLColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["mane_url_#{i}"] = {
        default: -> ''
        getFunc: "ManeURL#{i}"
        type: 'URL'
    }

    PPM2.PonyDataRegistry["tail_detail_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "TailDetailColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["tail_url_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "TailURLColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["tail_url_#{i}"] = {
        default: -> ''
        getFunc: "TailURL#{i}"
        type: 'URL'
    }

    PPM2.PonyDataRegistry["tail_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "TailColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["lower_mane_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "LowerManeColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["lower_mane_url_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "LowerManeURLColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["lower_mane_url_#{i}"] = {
        default: -> ''
        getFunc: "LowerManeURL#{i}"
        type: 'URL'
    }

    PPM2.PonyDataRegistry["upper_mane_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "UpperManeColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["upper_mane_url_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "UpperManeURLColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["upper_mane_url_#{i}"] = {
        default: -> ''
        getFunc: "UpperManeURL#{i}"
        type: 'URL'
    }

    PPM2.PonyDataRegistry["lower_mane_detail_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "LowerManeDetailColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["upper_mane_detail_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "UpperManeDetailColor#{i}"
        type: 'COLOR'
    }

for i = 1, PPM2.MAX_BODY_DETAILS
    PPM2.PonyDataRegistry["body_detail_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "BodyDetailColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["body_detail_#{i}"] = {
        default: -> 0
        getFunc: "BodyDetail#{i}"
        enum: [arg for arg in *PPM2.BodyDetailsEnum]
    }

    PPM2.PonyDataRegistry["body_detail_url_#{i}"] = {
        default: -> ''
        getFunc: "BodyDetailURL#{i}"
        type: 'URL'
    }

    PPM2.PonyDataRegistry["body_detail_url_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "BodyDetailURLColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["body_detail_glow_#{i}"] = {
        default: -> false
        getFunc: "BodyDetailGlow#{i}"
        type: 'BOOLEAN'
    }

    PPM2.PonyDataRegistry["body_detail_glow_strength_#{i}"] = {
        default: -> 1
        getFunc: "BodyDetailGlowStrength#{i}"
        type: 'FLOAT'
        min: 0
        max: 1
    }

for i = 1, PPM2.MAX_TATTOOS
    PPM2.PonyDataRegistry["tattoo_type_#{i}"] = {
        default: -> 0
        getFunc: "TattooType#{i}"
        enum: [arg for arg in *PPM2.TATTOOS_REGISTRY]
    }

    PPM2.PonyDataRegistry["tattoo_posx_#{i}"] = {
        default: -> 0
        getFunc: "TattooPosX#{i}"
        min: -100
        max: 100
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["tattoo_posy_#{i}"] = {
        default: -> 0
        getFunc: "TattooPosY#{i}"
        fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, -100, 100)
        min: -100
        max: 100
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["tattoo_rotate_#{i}"] = {
        default: -> 0
        getFunc: "TattooRotate#{i}"
        min: -180
        max: 180
        type: 'INT'
    }

    PPM2.PonyDataRegistry["tattoo_scalex_#{i}"] = {
        default: -> 1
        getFunc: "TattooScaleX#{i}"
        min: 0
        max: 10
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["tattoo_glow_strength_#{i}"] = {
        default: -> 1
        getFunc: "TattooGlowStrength#{i}"
        min: 0
        max: 1
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["tattoo_scaley_#{i}"] = {
        default: -> 1
        getFunc: "TattooScaleY#{i}"
        min: 0
        max: 10
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["tattoo_glow_#{i}"] = {
        default: -> false
        getFunc: "TattooGlow#{i}"
        type: 'BOOLEAN'
    }

    PPM2.PonyDataRegistry["tattoo_over_detail_#{i}"] = {
        default: -> false
        getFunc: "TattooOverDetail#{i}"
        type: 'BOOLEAN'
    }

    PPM2.PonyDataRegistry["tattoo_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "TattooColor#{i}"
        type: 'COLOR'
    }

for ttype in *{'Body', 'Horn', 'Wings', 'BatWingsSkin', 'Socks', 'Mane', 'Tail', 'UpperMane', 'LowerMane'}
    PPM2.PonyDataRegistry[ttype\lower() .. '_phong_exponent'] = {
        default: -> 3
        getFunc: ttype .. 'PhongExponent'
        min: 0.04
        max: 10
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry[ttype\lower() .. '_phong_boost'] = {
        default: -> 0.09
        getFunc: ttype .. 'PhongBoost'
        min: 0.01
        max: 1
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry[ttype\lower() .. '_phong_front'] = {
        default: -> 1
        getFunc: ttype .. 'PhongFront'
        min: 0
        max: 20
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry[ttype\lower() .. '_phong_middle'] = {
        default: -> 5
        getFunc: ttype .. 'PhongMiddle'
        min: 0
        max: 20
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry[ttype\lower() .. '_phong_sliding'] = {
        default: -> 10
        getFunc: ttype .. 'PhongSliding'
        min: 0
        max: 20
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry[ttype\lower() .. '_phong_tint'] = {
        default: -> Color(255, 200, 200)
        getFunc: ttype .. 'PhongTint'
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry[ttype\lower() .. '_lightwarp_texture'] = {
        default: -> 0
        getFunc: ttype .. 'Lightwarp'
        enum: [arg for arg in *PPM2.AvaliableLightwarps]
    }

    PPM2.PonyDataRegistry[ttype\lower() .. '_lightwarp_texture_url'] = {
        default: -> ''
        getFunc: ttype .. 'LightwarpURL'
        type: 'URL'
    }

for {:flex, :active} in *PPM2.PonyFlexController.FLEX_LIST
    continue if not active
    PPM2.PonyDataRegistry["flex_disable_#{flex\lower()}"] = {
        default: -> false
        getFunc: "DisableFlex#{flex}"
        type: 'BOOLEAN'
    }

for key, value in pairs PPM2.PonyDataRegistry
    if value.enum
        value.min = 0
        value.max = #value.enum - 1
        value.type = 'INT'

    switch value.type
        when 'INT'
            error("Variable #{key} has invalid minimal value (#{type(value.min)})") if type(value.min) ~= 'number'
            error("Variable #{max} has invalid maximal value (#{type(value.max)})") if type(value.max) ~= 'number'
            value.fix = INT_FIXER(value.default, value.min, value.max)
            if value.min >= 0
                selectBits = 16
                while 2 ^ (selectBits - 1) > value.max
                    selectBits -= 1
                value.read = rUInt(selectBits, value.min, value.max)
                value.write = wUInt(value.default(), selectBits)
            else
                delta = math.max(value.max, -value.min)
                selectBits = 16
                while 2 ^ (selectBits - 1) > delta
                    selectBits -= 1
                value.read = rInt(selectBits, value.min, value.max)
                value.write = wInt(value.default(), selectBits)
        when 'FLOAT'
            error("Variable #{key} has invalid minimal value (#{type(value.min)})") if type(value.min) ~= 'number'
            error("Variable #{max} has invalid maximal value (#{type(value.max)})") if type(value.max) ~= 'number'
            value.fix = FLOAT_FIXER(value.default, value.min, value.max)
            value.read = rFloat(value.min, value.max)
            value.write = (arg = value.default()) -> wFloat(arg)
        when 'URL'
            value.fix = URL_FIXER
            value.read = rURL
            value.write = wString
        when 'BOOLEAN'
            value.fix = (arg = value.default()) -> tobool(arg)
            value.read = rBool
            value.write = wBool
        when 'COLOR'
            {:r, :g, :b, :a} = value.default()
            value.fix = COLOR_FIXER(r, g, b, a)
            value.read = rColor
            value.write = wColor
        else
            error("Unknown variable type - #{value.type} for #{key}")

    error("Data has no fix function: #{key}") if not value.fix

--
-- Copyright (C) 2017 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.
--

wUInt = (def = 0, size = 8) ->
    return (arg = def) -> net.WriteUInt(arg, size)

wInt = (def = 0, size = 8) ->
    return (arg = def) -> net.WriteInt(arg, size)

rUInt = (size = 8, min = 0, max = 255) ->
    return -> math.Clamp(net.ReadUInt(size), min, max)

rInt = (size = 8, min = -128, max = 127) ->
    return -> math.Clamp(net.ReadInt(size), min, max)

rFloat = (min = 0, max = 255) ->
    return -> math.Clamp(net.ReadFloat(), min, max)

wFloat = net.WriteFloat
rSEnt = net.ReadStrongEntity
wSEnt = net.WriteStrongEntity
rBool = net.ReadBool
wBool = net.WriteBool
rColor = net.ReadColor
wColor = net.WriteColor
rString = net.ReadString
wString = net.WriteString

COLOR_FIXER = (r = 255, g = 255, b = 255, a = 255) ->
    func = (arg = Color(r, g, b, a)) ->
        if type(arg) ~= 'table'
            return Color(255, 255, 255)
        else
            {:r, :g, :b, :a} = arg
            if r and g and b and a
                return Color(r, g, b, a)
            else
                return Color(255, 255, 255)
    return func

URL_FIXER = (arg = '') ->
    arg = tostring(arg)
    if arg\find('^https?://')
        return arg
    else
        return ''

rURL = -> URL_FIXER(rString())

FLOAT_FIXER = (def = 1, min = 0, max = 1) ->
    defFunc = -> def if type(def) ~= 'function'
    defFunc = def if type(def) == 'function'
    return (arg = defFunc()) -> math.Clamp(tonumber(arg) or defFunc(), min, max)

INT_FIXER = (def = 1, min = 0, max = 1) ->
    defFunc = -> def if type(def) ~= 'function'
    defFunc = def if type(def) == 'function'
    return (arg = defFunc()) -> math.floor(math.Clamp(tonumber(arg) or defFunc(), min, max))

PPM2.PonyDataRegistry = {
    'age': {
        default: -> PPM2.AGE_ADULT
        getFunc: 'Age'
        enum: {'FILLY', 'ADULT', 'MATURE'}
    }

    'race': {
        default: -> PPM2.RACE_EARTH
        getFunc: 'Race'
        enum: [arg for arg in *PPM2.RACE_ENUMS]
    }

    'wings_type': {
        default: -> 0
        getFunc: 'WingsType'
        enum: [arg for arg in *PPM2.AvaliablePonyWings]
    }

    'gender': {
        default: -> PPM2.GENDER_FEMALE
        getFunc: 'Gender'
        enum: [arg for arg in *PPM2.AGE_ENUMS]
    }

    'weight': {
        default: -> 1
        getFunc: 'Weight'
        min: PPM2.MIN_WEIGHT
        max: PPM2.MAX_WEIGHT
        type: 'FLOAT'
    }

    'ponysize': {
        default: -> 1
        getFunc: 'PonySize'
        min: PPM2.MIN_SCALE
        max: PPM2.MAX_SCALE
        type: 'FLOAT'
    }

    'necksize': {
        default: -> 1
        getFunc: 'NeckSize'
        min: PPM2.MIN_NECK
        max: PPM2.MAX_NECK
        type: 'FLOAT'
    }

    'legssize': {
        default: -> 1
        getFunc: 'LegsSize'
        min: PPM2.MIN_LEGS
        max: PPM2.MAX_LEGS
        type: 'FLOAT'
    }

    'male_buff': {
        default: -> PPM2.DEFAULT_MALE_BUFF
        getFunc: 'MaleBuff'
        min: PPM2.MIN_MALE_BUFF
        max: PPM2.MAX_MALE_BUFF
        type: 'FLOAT'
    }

    'eyelash': {
        default: -> 0
        getFunc: 'EyelashType'
        enum: [arg for arg in *PPM2.EyelashTypes]
    }

    'tail': {
        default: -> 0
        getFunc: 'TailType'
        enum: [arg for arg in *PPM2.AvaliableTails]
    }

    'tail_new': {
        default: -> 0
        getFunc: 'TailTypeNew'
        enum: [arg for arg in *PPM2.AvaliableTailsNew]
    }

    'mane': {
        default: -> 0
        getFunc: 'ManeType'
        enum: [arg for arg in *PPM2.AvaliableUpperManes]
    }

    'mane_new': {
        default: -> 0
        getFunc: 'ManeTypeNew'
        enum: [arg for arg in *PPM2.AvaliableUpperManesNew]
    }

    'manelower': {
        default: -> 0
        getFunc: 'ManeTypeLower'
        enum: [arg for arg in *PPM2.AvaliableLowerManes]
    }

    'manelower_new': {
        default: -> 0
        getFunc: 'ManeTypeLowerNew'
        enum: [arg for arg in *PPM2.AvaliableLowerManesNew]
    }

    'socks_texture': {
        default: -> 0
        getFunc: 'SocksTexture'
        enum: [arg for arg in *PPM2.SocksTypes]
    }

    'socks_texture_url': {
        default: -> ''
        getFunc: 'SocksTextureURL'
        type: 'URL'
    }

    'tailsize': {
        default: -> 1
        getFunc: 'TailSize'
        min: PPM2.MIN_TAIL_SIZE
        max: PPM2.MAX_TAIL_SIZE
        type: 'FLOAT'
    }

    'cmark': {
        default: -> true
        getFunc: 'CMark'
        type: 'BOOLEAN'
    }

    'cmark_size': {
        default: -> 1
        getFunc: 'CMarkSize'
        min: 0
        max: 1
        type: 'FLOAT'
    }

    'cmark_color': {
        default: -> Color(255, 255, 255)
        getFunc: 'CMarkColor'
        type: 'COLOR'
    }

    'fangs': {
        default: -> false
        getFunc: 'Fangs'
        type: 'BOOLEAN'
    }

    'bat_pony_ears': {
        default: -> false
        getFunc: 'BatPonyEars'
        type: 'BOOLEAN'
    }

    'claw_teeth': {
        default: -> false
        getFunc: 'ClawTeeth'
        type: 'BOOLEAN'
    }

    'cmark_type': {
        default: -> 4
        getFunc: 'CMarkType'
        enum: [arg for arg in *PPM2.DefaultCutiemarks]
    }

    'cmark_url': {
        default: -> ''
        getFunc: 'CMarkURL'
        type: 'URL'
    }

    'body': {
        default: -> Color(255, 255, 255)
        getFunc: 'BodyColor'
        type: 'COLOR'
    }

    'horn_color': {
        default: -> Color(255, 255, 255)
        getFunc: 'HornColor'
        type: 'COLOR'
    }

    'wings_color': {
        default: -> Color(255, 255, 255)
        getFunc: 'WingsColor'
        type: 'COLOR'
    }

    'separate_wings': {
        default: -> false
        getFunc: 'SeparateWings'
        type: 'BOOLEAN'
    }

    'separate_horn': {
        default: -> false
        getFunc: 'SeparateHorn'
        type: 'BOOLEAN'
    }

    'use_horn_detail': {
        default: -> false
        getFunc: 'UseHornDetail'
        type: 'BOOLEAN'
    }

    'horn_glow': {
        default: -> false
        getFunc: 'HornGlow'
        type: 'BOOLEAN'
    }

    'horn_glow_strength': {
        default: -> 1
        getFunc: 'HornGlowSrength'
        min: 0
        max: 1
        type: 'FLOAT'
    }

    'horn_detail_color': {
        default: -> Color(255, 255, 255)
        getFunc: 'HornDetailColor'
        type: 'COLOR'
    }

    'separate_eyes': {
        default: -> false
        getFunc: 'SeparateEyes'
        type: 'BOOLEAN'
    }

    'separate_mane': {
        default: -> false
        getFunc: 'SeparateMane'
        type: 'BOOLEAN'
    }

    'socks': {
        default: -> false
        getFunc: 'Socks'
        type: 'BOOLEAN'
    }

    'new_male_muzzle': {
        default: -> true
        getFunc: 'NewMuzzle'
        type: 'BOOLEAN'
    }

    'noflex': {
        default: -> false
        getFunc: 'NoFlex'
        type: 'BOOLEAN'
    }

    'socks_model': {
        default: -> false
        getFunc: 'SocksAsModel'
        type: 'BOOLEAN'
    }

    'socks_model_color': {
        default: -> Color(255, 255, 255)
        getFunc: 'SocksColor'
        type: 'COLOR'
    }

    'suit': {
        default: -> 0
        getFunc: 'Bodysuit'
        enum: [arg for arg in *PPM2.AvaliablePonySuits]
    }

    'left_wing_size': {
        default: -> 1
        getFunc: 'LWingSize'
        min: PPM2.MIN_WING
        max: PPM2.MAX_WING
        type: 'FLOAT'
    }

    'left_wing_x': {
        default: -> 0
        getFunc: 'LWingX'
        min: PPM2.MIN_WINGX
        max: PPM2.MAX_WINGX
        type: 'FLOAT'
    }

    'left_wing_y': {
        default: -> 0
        getFunc: 'LWingY'
        min: PPM2.MIN_WINGY
        max: PPM2.MAX_WINGY
        type: 'FLOAT'
    }

    'left_wing_z': {
        default: -> 0
        getFunc: 'LWingZ'
        min: PPM2.MIN_WINGZ
        max: PPM2.MAX_WINGZ
        type: 'FLOAT'
    }

    'right_wing_size': {
        default: -> 1
        getFunc: 'RWingSize'
        min: PPM2.MIN_WING
        max: PPM2.MAX_WING
        type: 'FLOAT'
    }

    'right_wing_x': {
        default: -> 0
        getFunc: 'RWingX'
        min: PPM2.MIN_WINGX
        max: PPM2.MAX_WINGX
        type: 'FLOAT'
    }

    'right_wing_y': {
        default: -> 0
        getFunc: 'RWingY'
        min: PPM2.MIN_WINGY
        max: PPM2.MAX_WINGY
        type: 'FLOAT'
    }

    'right_wing_z': {
        default: -> 0
        getFunc: 'RWingZ'
        min: PPM2.MIN_WINGZ
        max: PPM2.MAX_WINGZ
        type: 'FLOAT'
    }

    'teeth_color': {
        default: -> Color(255, 255, 255)
        getFunc: 'TeethColor'
        type: 'COLOR'
    }

    'mouth_color': {
        default: -> Color(219, 65, 155)
        getFunc: 'MouthColor'
        type: 'COLOR'
    }

    'tongue_color': {
        default: -> Color(235, 131, 59)
        getFunc: 'TongueColor'
        type: 'COLOR'
    }

    'bat_wing_color': {
        default: -> Color(255, 255, 255)
        getFunc: 'BatWingColor'
        type: 'COLOR'
    }

    'bat_wing_skin_color': {
        default: -> Color(255, 255, 255)
        getFunc: 'BatWingSkinColor'
        type: 'COLOR'
    }

    'separate_horn_phong': {
        default: -> false
        getFunc: 'SeparateHornPhong'
        type: 'BOOLEAN'
    }

    'separate_wings_phong': {
        default: -> false
        getFunc: 'SeparateWingsPhong'
        type: 'BOOLEAN'
    }

    'separate_mane_phong': {
        default: -> false
        getFunc: 'SeparateManePhong'
        type: 'BOOLEAN'
    }

    'separate_tail_phong': {
        default: -> false
        getFunc: 'SeparateTailPhong'
        type: 'BOOLEAN'
    }

    'alternative_fangs': {
        default: -> false
        getFunc: 'AlternativeFangs'
        type: 'BOOLEAN'
    }

    'hoof_fluffers': {
        default: -> false
        getFunc: 'HoofFluffers'
        type: 'BOOLEAN'
    }

    'hoof_fluffers_strength': {
        default: -> 1
        getFunc: 'HoofFluffersStrength'
        min: 0
        max: 1
        type: 'FLOAT'
    }

    'ears_size': {
        default: -> 1
        getFunc: 'EarsSize'
        min: 0.1
        max: 2
        type: 'FLOAT'
    }

    'bat_pony_ears_strength': {
        default: -> 1
        getFunc: 'BatPonyEarsStrength'
        min: 0
        max: 1
        type: 'FLOAT'
    }

    'fangs_strength': {
        default: -> 1
        getFunc: 'FangsStrength'
        min: 0
        max: 1
        type: 'FLOAT'
    }

    'clawteeth_strength': {
        default: -> 1
        getFunc: 'ClawTeethStrength'
        min: 0
        max: 1
        type: 'FLOAT'
    }
}

for {internal, publicName} in *{{'_left', 'Left'}, {'_right', 'Right'}, {'', ''}}
    PPM2.PonyDataRegistry["eye_url#{internal}"] = {
        default: -> ''
        getFunc: "EyeURL#{publicName}"
        type: 'URL'
    }

    PPM2.PonyDataRegistry["eye_bg#{internal}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "EyeBackground#{publicName}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["eye_hole#{internal}"] = {
        default: -> Color(0, 0, 0)
        getFunc: "EyeHole#{publicName}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["eye_iris1#{internal}"] = {
        default: -> Color(200, 200, 200)
        getFunc: "EyeIrisTop#{publicName}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["eye_iris2#{internal}"] = {
        default: -> Color(200, 200, 200)
        getFunc: "EyeIrisBottom#{publicName}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["eye_irisline1#{internal}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "EyeIrisLine1#{publicName}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["eye_irisline2#{internal}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "EyeIrisLine2#{publicName}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["eye_reflection#{internal}"] = {
        default: -> Color(255, 255, 255, 127)
        getFunc: "EyeReflection#{publicName}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["eye_effect#{internal}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "EyeEffect#{publicName}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["eye_lines#{internal}"] = {
        default: -> true
        getFunc: "EyeLines#{publicName}"
        type: 'BOOLEAN'
    }

    PPM2.PonyDataRegistry["eye_iris_size#{internal}"] = {
        default: -> 1
        getFunc: "IrisSize#{publicName}"
        min: PPM2.MIN_IRIS
        max: PPM2.MAX_IRIS
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["eye_derp#{internal}"] = {
        default: -> false
        getFunc: "DerpEyes#{publicName}"
        type: 'BOOLEAN'
    }

    PPM2.PonyDataRegistry["eye_derp_strength#{internal}"] = {
        default: -> 1
        getFunc: "DerpEyesStrength#{publicName}"
        min: PPM2.MIN_DERP_STRENGTH
        max: PPM2.MAX_DERP_STRENGTH
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["eye_type#{internal}"] = {
        default: -> 0
        getFunc: "EyeType#{publicName}"
        enum: [arg for arg in *PPM2.AvaliableEyeTypes]
    }

    PPM2.PonyDataRegistry["hole_width#{internal}"] = {
        default: -> 1
        getFunc: "HoleWidth#{publicName}"
        min: PPM2.MIN_PUPIL_SIZE
        max: PPM2.MAX_PUPIL_SIZE
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["hole_height#{internal}"] = {
        default: -> 1
        getFunc: "HoleHeight#{publicName}"
        min: PPM2.MIN_PUPIL_SIZE
        max: PPM2.MAX_PUPIL_SIZE
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["iris_width#{internal}"] = {
        default: -> 1
        getFunc: "IrisWidth#{publicName}"
        min: PPM2.MIN_PUPIL_SIZE
        max: PPM2.MAX_PUPIL_SIZE
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["iris_height#{internal}"] = {
        default: -> 1
        getFunc: "IrisHeight#{publicName}"
        min: PPM2.MIN_PUPIL_SIZE
        max: PPM2.MAX_PUPIL_SIZE
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["hole_shiftx#{internal}"] = {
        default: -> 0
        getFunc: "HoleShiftX#{publicName}"
        min: PPM2.MIN_HOLE_SHIFT
        max: PPM2.MAX_HOLE_SHIFT
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["hole_shifty#{internal}"] = {
        default: -> 0
        getFunc: "HoleShiftY#{publicName}"
        min: PPM2.MIN_HOLE_SHIFT
        max: PPM2.MAX_HOLE_SHIFT
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["eye_hole_size#{internal}"] = {
        default: -> .8
        getFunc: "HoleSize#{publicName}"
        min: PPM2.MIN_HOLE
        max: PPM2.MAX_HOLE
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["eye_rotation#{internal}"] = {
        default: -> 0
        getFunc: "EyeRotation#{publicName}"
        min: PPM2.MIN_EYE_ROTATION
        max: PPM2.MAX_EYE_ROTATION
        type: 'INT'
    }

for i = 1, 3
    PPM2.PonyDataRegistry["horn_url_#{i}"] = {
        default: -> ''
        getFunc: "HornURL#{i}"
        type: 'URL'
    }

    PPM2.PonyDataRegistry["bat_wing_url_#{i}"] = {
        default: -> ''
        getFunc: "BatWingURL#{i}"
        type: 'URL'
    }

    PPM2.PonyDataRegistry["bat_wing_skin_url_#{i}"] = {
        default: -> ''
        getFunc: "BatWingSkinURL#{i}"
        type: 'URL'
    }

    PPM2.PonyDataRegistry["wings_url_#{i}"] = {
        default: -> ''
        getFunc: "WingsURL#{i}"
        type: 'URL'
    }

    PPM2.PonyDataRegistry["horn_url_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "HornURLColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["bat_wing_url_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "BatWingURLColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["bat_wing_skin_url_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "BatWingSkinURLColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["wings_url_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "WingsURLColor#{i}"
        type: 'COLOR'
    }

for i = 1, 6
    PPM2.PonyDataRegistry["socks_detail_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "SocksDetailColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["mane_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "ManeColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["mane_detail_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "ManeDetailColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["mane_url_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "ManeURLColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["mane_url_#{i}"] = {
        default: -> ''
        getFunc: "ManeURL#{i}"
        type: 'URL'
    }

    PPM2.PonyDataRegistry["tail_detail_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "TailDetailColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["tail_url_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "TailURLColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["tail_url_#{i}"] = {
        default: -> ''
        getFunc: "TailURL#{i}"
        type: 'URL'
    }

    PPM2.PonyDataRegistry["tail_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "TailColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["lower_mane_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "LowerManeColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["lower_mane_url_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "LowerManeURLColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["lower_mane_url_#{i}"] = {
        default: -> ''
        getFunc: "LowerManeURL#{i}"
        type: 'URL'
    }

    PPM2.PonyDataRegistry["upper_mane_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "UpperManeColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["upper_mane_url_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "UpperManeURLColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["upper_mane_url_#{i}"] = {
        default: -> ''
        getFunc: "UpperManeURL#{i}"
        type: 'URL'
    }

    PPM2.PonyDataRegistry["lower_mane_detail_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "LowerManeDetailColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["upper_mane_detail_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "UpperManeDetailColor#{i}"
        type: 'COLOR'
    }

for i = 1, PPM2.MAX_BODY_DETAILS
    PPM2.PonyDataRegistry["body_detail_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "BodyDetailColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["body_detail_#{i}"] = {
        default: -> 0
        getFunc: "BodyDetail#{i}"
        enum: [arg for arg in *PPM2.BodyDetailsEnum]
    }

    PPM2.PonyDataRegistry["body_detail_url_#{i}"] = {
        default: -> ''
        getFunc: "BodyDetailURL#{i}"
        type: 'URL'
    }

    PPM2.PonyDataRegistry["body_detail_url_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "BodyDetailURLColor#{i}"
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry["body_detail_glow_#{i}"] = {
        default: -> false
        getFunc: "BodyDetailGlow#{i}"
        type: 'BOOLEAN'
    }

    PPM2.PonyDataRegistry["body_detail_glow_strength_#{i}"] = {
        default: -> 1
        getFunc: "BodyDetailGlowStrength#{i}"
        type: 'FLOAT'
        min: 0
        max: 1
    }

for i = 1, PPM2.MAX_TATTOOS
    PPM2.PonyDataRegistry["tattoo_type_#{i}"] = {
        default: -> 0
        getFunc: "TattooType#{i}"
        enum: [arg for arg in *PPM2.TATTOOS_REGISTRY]
    }

    PPM2.PonyDataRegistry["tattoo_posx_#{i}"] = {
        default: -> 0
        getFunc: "TattooPosX#{i}"
        min: -100
        max: 100
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["tattoo_posy_#{i}"] = {
        default: -> 0
        getFunc: "TattooPosY#{i}"
        fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, -100, 100)
        min: -100
        max: 100
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["tattoo_rotate_#{i}"] = {
        default: -> 0
        getFunc: "TattooRotate#{i}"
        min: -180
        max: 180
        type: 'INT'
    }

    PPM2.PonyDataRegistry["tattoo_scalex_#{i}"] = {
        default: -> 1
        getFunc: "TattooScaleX#{i}"
        min: 0
        max: 10
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["tattoo_glow_strength_#{i}"] = {
        default: -> 1
        getFunc: "TattooGlowStrength#{i}"
        min: 0
        max: 1
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["tattoo_scaley_#{i}"] = {
        default: -> 1
        getFunc: "TattooScaleY#{i}"
        min: 0
        max: 10
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry["tattoo_glow_#{i}"] = {
        default: -> false
        getFunc: "TattooGlow#{i}"
        type: 'BOOLEAN'
    }

    PPM2.PonyDataRegistry["tattoo_over_detail_#{i}"] = {
        default: -> false
        getFunc: "TattooOverDetail#{i}"
        type: 'BOOLEAN'
    }

    PPM2.PonyDataRegistry["tattoo_color_#{i}"] = {
        default: -> Color(255, 255, 255)
        getFunc: "TattooColor#{i}"
        type: 'COLOR'
    }

for ttype in *{'Body', 'Horn', 'Wings', 'BatWingsSkin', 'Socks', 'Mane', 'Tail', 'UpperMane', 'LowerMane'}
    PPM2.PonyDataRegistry[ttype\lower() .. '_phong_exponent'] = {
        default: -> 3
        getFunc: ttype .. 'PhongExponent'
        min: 0.04
        max: 10
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry[ttype\lower() .. '_phong_boost'] = {
        default: -> 0.09
        getFunc: ttype .. 'PhongBoost'
        min: 0.01
        max: 1
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry[ttype\lower() .. '_phong_front'] = {
        default: -> 1
        getFunc: ttype .. 'PhongFront'
        min: 0
        max: 20
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry[ttype\lower() .. '_phong_middle'] = {
        default: -> 5
        getFunc: ttype .. 'PhongMiddle'
        min: 0
        max: 20
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry[ttype\lower() .. '_phong_sliding'] = {
        default: -> 10
        getFunc: ttype .. 'PhongSliding'
        min: 0
        max: 20
        type: 'FLOAT'
    }

    PPM2.PonyDataRegistry[ttype\lower() .. '_phong_tint'] = {
        default: -> Color(255, 200, 200)
        getFunc: ttype .. 'PhongTint'
        type: 'COLOR'
    }

    PPM2.PonyDataRegistry[ttype\lower() .. '_lightwarp_texture'] = {
        default: -> 0
        getFunc: ttype .. 'Lightwarp'
        enum: [arg for arg in *PPM2.AvaliableLightwarps]
    }

    PPM2.PonyDataRegistry[ttype\lower() .. '_lightwarp_texture_url'] = {
        default: -> ''
        getFunc: ttype .. 'LightwarpURL'
        type: 'URL'
    }

for {:flex, :active} in *PPM2.PonyFlexController.FLEX_LIST
    continue if not active
    PPM2.PonyDataRegistry["flex_disable_#{flex\lower()}"] = {
        default: -> false
        getFunc: "DisableFlex#{flex}"
        type: 'BOOLEAN'
    }

for key, value in pairs PPM2.PonyDataRegistry
    if value.enum
        value.min = 0
        value.max = #value.enum - 1
        value.type = 'INT'

    switch value.type
        when 'INT'
            value.fix = INT_FIXER(value.default, value.min, value.max)
            if value.min >= 0
                selectBits = 16
                while 2 ^ (selectBits - 1) > value.max
                    selectBits -= 1
                value.read = rUInt(selectBits, value.min, value.max)
                value.write = wUInt(value.default(), selectBits)
            else
                delta = math.max(value.max, -value.min)
                selectBits = 16
                while 2 ^ (selectBits - 1) > delta
                    selectBits -= 1
                value.read = rInt(selectBits, value.min, value.max)
                value.write = wInt(value.default(), selectBits)
        when 'FLOAT'
            value.fix = FLOAT_FIXER(value.default, value.min, value.max)
            value.read = rFloat(value.min, value.max)
            value.write = (arg = value.default()) -> wFloat(arg)
        when 'URL'
            value.fix = URL_FIXER
            value.read = rURL
            value.write = wString
        when 'BOOLEAN'
            value.fix = (arg = value.default()) -> tobool(arg)
            value.read = rBool
            value.write = wBool
        when 'COLOR'
            {:r, :g, :b, :a} = value.default()
            value.fix = COLOR_FIXER(r, g, b, a)
            value.read = rColor
            value.write = wColor
        else
            error("Unknown variable type - #{value.type} for #{key}")

    error("Data has no fix function: #{key}") if not value.fix