utf8: add upsidedown

utf8: add upsidedown

Former-commit-id: b28606198d067dee0483d146c7ab11ae21f861bf [formerly 1459146fe9f4013717237e87e49b9116cf0ec175]
Former-commit-id: 3ab7e733509cc0f34f06f330234be65658e0a9a2

html2unicode = require'html'
math = require'math'

an = [[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789,.?!"'`()[]{}<>&_]]
ci = 'ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ⓪①②③④⑤⑥⑦⑧⑨'
bl = '𝔄𝔅ℭ𝔇𝔈𝔉𝔊ℌℑ𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔ℜ𝔖𝔗𝔘𝔙𝔚𝔛𝔜ℨ𝔞𝔟𝔠𝔡𝔢𝔣𝔤𝔥𝔦𝔧𝔨𝔩𝔪𝔫𝔬𝔭𝔮𝔯𝔰𝔱𝔲𝔳𝔴𝔵𝔶𝔷'
ud = [[∀BƆDƎℲפHIſK˥WNOԀQRS┴∩ΛMX⅄Zɐqɔpǝɟƃɥᴉɾʞlɯuodbɹsʇnʌʍxʎz0ƖᄅƐㄣϛ9ㄥ86'˙¿¡,,,)(][}{><⅋‾]]

an2ci = {}
-- Circled letters are 3 bytes, just use sub string
for i=1, #an
  f = i*3+1-3
  t = i*3
  an2ci[an\sub(i,i)] = ci\sub(f, t)

an2bl = {}
i=1
-- Since blackletters have varying byte length, use the common lua pattern to find multibyte chars
for uchar in string.gfind(bl, "([%z\1-\127\194-\244][\128-\191]*)")
  an2bl[an\sub(i,i)] = uchar
  i = i +1

an2ud = {}
i=1
for uchar in string.gfind(ud, "([%z\1-\127\194-\244][\128-\191]*)")
  an2ud[an\sub(i,i)] = uchar
  i = i +1

codepoints = (str) ->
  str\gmatch("[%z\1-\127\194-\244][\128-\191]*")

unichr = (n) ->
  html2unicode('&#x%x;'\format(n))

wireplace = (offset, arg) ->
    s = arg or ''
    t = {}
    for i = 1, #s
      bc = string.byte(s, i, i)
      -- Replace space width ideographic space for fullwidth offset
      if bc == 32 and offset == 0xFEE0
        t[#t + 1] = '\227\128\128'
      elseif bc == 32
        t[#t + 1] = ' '
      elseif bc < 0x80 then
        t[#t + 1] = html2unicode("&#" .. (offset + bc) .. ";")
      else
        t[#t + 1] = s\sub(i, i)

    table.concat(t, "")

remap = (map, s) ->
  table.concat [map[s\sub(i,i)] or s\sub(i,i) for i=1, #s], ''

zalgo = (text, intensity=50) ->
  zalgo_chars = {}
  for i=0x0300, 0x036f
    zalgo_chars[i-0x2ff] = unichr(i)

  zalgo_chars[#zalgo_chars + 1] = unichr(0x0488)
  zalgo_chars[#zalgo_chars + 0] = unichr(0x0489)

  zalgoized = {}
  for letter in codepoints(text)
    zalgoized[#zalgoized + 1] = letter
    zalgo_num = math.random(1, intensity)
    for i=1, zalgo_num
      zalgoized[#zalgoized + 1] = zalgo_chars[math.random(1, #zalgo_chars)]
  table.concat(zalgoized)

PRIVMSG:
  '^%pwide (.+)$': (source, destination, arg) =>
    @Msg 'privmsg', destination, source, wireplace(0xFEE0, arg)
  '^%pblackletter (.+)$': (source, destination, arg) =>
    @Msg 'privmsg', destination, source, remap(an2bl, arg)
  '^%pcircled (.+)$': (source, destination, arg) =>
    @Msg 'privmsg', destination, source, remap(an2ci, arg)
  '^%pzalgo (.+)$': (source, destination, arg) =>
    @Msg 'privmsg', destination, source, zalgo(arg, 10)
  '^%pupsidedown (.+)$': (source, destination, arg) =>
    @Msg 'privmsg', destination, source, remap(an2ud, arg)

html2unicode = require'html'
math = require'math'

an = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
ci = 'ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ⓪①②③④⑤⑥⑦⑧⑨'
bl = '𝔄𝔅ℭ𝔇𝔈𝔉𝔊ℌℑ𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔ℜ𝔖𝔗𝔘𝔙𝔚𝔛𝔜ℨ𝔞𝔟𝔠𝔡𝔢𝔣𝔤𝔥𝔦𝔧𝔨𝔩𝔪𝔫𝔬𝔭𝔮𝔯𝔰𝔱𝔲𝔳𝔴𝔵𝔶𝔷'

an2ci = {}
-- Circled letters are 3 bytes, just use sub string
for i=1, #an
  f = i*3+1-3
  t = i*3
  an2ci[an\sub(i,i)] = ci\sub(f, t)

an2bl = {}
i=1
-- Since blackletters have varying byte length, use the common lua pattern to find multibyte chars
for uchar in string.gfind(bl, "([%z\1-\127\194-\244][\128-\191]*)")
  an2bl[an\sub(i,i)] = uchar
  i = i +1

codepoints = (str) ->
  str\gmatch("[%z\1-\127\194-\244][\128-\191]*")

unichr = (n) ->
  html2unicode('&#x%x;'\format(n))

wireplace = (offset, arg) ->
    s = arg or ''
    t = {}
    for i = 1, #s
      bc = string.byte(s, i, i)
      -- Replace space width ideographic space for fullwidth offset
      if bc == 32 and offset == 0xFEE0
        t[#t + 1] = '\227\128\128'
      elseif bc == 32
        t[#t + 1] = ' '
      elseif bc < 0x80 then
        t[#t + 1] = html2unicode("&#" .. (offset + bc) .. ";")
      else
        t[#t + 1] = s\sub(i, i)

    table.concat(t, "")

remap = (map, s) ->
  table.concat [map[s\sub(i,i)] or s\sub(i,i) for i=1, #s], ''

zalgo = (text, intensity=50) ->
  zalgo_chars = {}
  for i=0x0300, 0x036f
    zalgo_chars[i-0x2ff] = unichr(i)

  zalgo_chars[#zalgo_chars + 1] = unichr(0x0488)
  zalgo_chars[#zalgo_chars + 0] = unichr(0x0489)

  zalgoized = {}
  for letter in codepoints(text)
    zalgoized[#zalgoized + 1] = letter
    zalgo_num = math.random(1, intensity)
    for i=1, zalgo_num
      zalgoized[#zalgoized + 1] = zalgo_chars[math.random(1, #zalgo_chars)]
  table.concat(zalgoized)

PRIVMSG:
  '^%pwide (.+)$': (source, destination, arg) =>
    @Msg 'privmsg', destination, source, wireplace(0xFEE0, arg)
  '^%pblackletter (.+)$': (source, destination, arg) =>
    @Msg 'privmsg', destination, source, remap(an2bl, arg)
  '^%pcircled (.+)$': (source, destination, arg) =>
    @Msg 'privmsg', destination, source, remap(an2ci, arg)
  '^%pzalgo (.+)$': (source, destination, arg) =>
    @Msg 'privmsg', destination, source, zalgo(arg, 10)


utf8: use new utf8 utils for less bugs

utf8: use new utf8 utils for less bugs

Former-commit-id: a5d5c2b7ad75d449de0471f7c9443d32bc4addce [formerly 381dc318eb4332ec9c436613009e317b24e02ee2]
Former-commit-id: a1b5429ca5cbc87b9091d9a0ab0968ace321202b

util = require'util'
html2unicode = require'html'

an = [[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789,.?!"'`()[]{}<>&_]]
charmaps = {
ci: 'ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ⓪①②③④⑤⑥⑦⑧⑨'
bl: '𝔄𝔅ℭ𝔇𝔈𝔉𝔊ℌℑ𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔ℜ𝔖𝔗𝔘𝔙𝔚𝔛𝔜ℨ𝔞𝔟𝔠𝔡𝔢𝔣𝔤𝔥𝔦𝔧𝔨𝔩𝔪𝔫𝔬𝔭𝔮𝔯𝔰𝔱𝔲𝔳𝔴𝔵𝔶𝔷'
ud: [[∀BƆDƎℲפHIſK˥WNOԀQRS┴∩ΛMX⅄Zɐqɔpǝɟƃɥᴉɾʞlɯuodbɹsʇnʌʍxʎz0ƖᄅƐㄣϛ9ㄥ86'˙¿¡,,,)(][}{><⅋‾]]
nc: [[🅐🅑🅒🅓🅔🅕🅖🅗🅘🅙🅚🅛🅜🅝🅞🅟🅠🅡🅢🅣🅤🅥🅦🅧🅨🅩🅐🅑🅒🅓🅔🅕🅖🅗🅘🅙🅚🅛🅜🅝🅞🅟🅠🅡🅢🅣🅤🅥🅦🅧🅨🅩⓿]]
sq: [[🄰🄱🄲🄳🄴🄵🄶🄷🄸🄹🄺🄻🄼🄽🄾🄿🅀🅁🅂🅃🅄🅅🅆🅇🅈🅉🄰🄱🄲🄳🄴🄵🄶🄷🄸🄹🄺🄻🄼🄽🄾🄿🅀🅁🅂🅃🅄🅅🅆🅇🅈🅉0123456789,⊡]]
ns: [[🅰🅱🅲🅳🅴🅵🅶🅷🅸🅹🅺🅻🅼🅽🅾🅿🆀🆁🆂🆃🆄🆅🆆🆇🆈🆉🅰🅱🅲🅳🅴🅵🅶🅷🅸🅹🅺🅻🅼🅽🅾🅿🆀🆁🆂🆃🆄🆅🆆🆇🆈🆉]]
ds: [[𝔸𝔹ℂ𝔻𝔼𝔽𝔾ℍ𝕀𝕁𝕂𝕃𝕄ℕ𝕆ℙℚℝ𝕊𝕋𝕌𝕍𝕎𝕏𝕐ℤ𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡]]
bo: [[𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗]]
bi: [[𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁𝒂𝒃𝒄𝒅𝒆𝒇𝒈𝒉𝒊𝒋𝒌𝒍𝒎𝒏𝒐𝒑𝒒𝒓𝒔𝒕𝒖𝒗𝒘𝒙𝒚𝒛0123456789]]
bs: [[𝓐𝓑𝓒𝓓𝓔𝓕𝓖𝓗𝓘𝓙𝓚𝓛𝓜𝓝𝓞𝓟𝓠𝓡𝓢𝓣𝓤𝓥𝓦𝓧𝓨𝓩𝓪𝓫𝓬𝓭𝓮𝓯𝓰𝓱𝓲𝓳𝓴𝓵𝓶𝓷𝓸𝓹𝓺𝓻𝓼𝓽𝓾𝓿𝔀𝔁𝔂𝔃]]
pt: [[⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵0⑴⑵⑶⑷⑸⑹⑺⑻⑼]]
}
codepoints = (str) ->
  str\gmatch("[%z\1-\127\194-\244][\128-\191]*")

-- Construct a table which can be used for lookup replacement later
-- Iterate over normal ascii and find counterparts in the weirdo strings as substrings
maps = {}
for charmap, chars in pairs charmaps
  i = 1
  maps[charmap] = {}
  for uchar in util.utf8.chars(chars)
    maps[charmap][an\sub(i,i)] = uchar
    i = i +1

unichr = (n) ->
  html2unicode('&#x%x;'\format(n))

wireplace = (offset, arg) ->
    s = arg or ''
    t = {}
    for i = 1, #s
      bc = string.byte(s, i, i)
      -- Replace space width ideographic space for fullwidth offset
      if bc == 32 and offset == 0xFEE0
        t[#t + 1] = '\227\128\128'
      elseif bc == 32
        t[#t + 1] = ' '
      elseif bc < 0x80 then
        t[#t + 1] = html2unicode("&#" .. (offset + bc) .. ";")
      else
        t[#t + 1] = s\sub(i, i)

    table.concat(t, "")

remap = (map, s) ->
  table.concat [map[s\sub(i,i)] or s\sub(i,i) for i=1, #s], ''

zalgo = (text, intensity=50) ->
  zalgo_chars = {}
  for i=0x0300, 0x036f
    zalgo_chars[i-0x2ff] = unichr(i)

  zalgo_chars[#zalgo_chars + 1] = unichr(0x0488)
  zalgo_chars[#zalgo_chars + 0] = unichr(0x0489)

  zalgoized = {}
  for letter in codepoints(text)
    zalgoized[#zalgoized + 1] = letter
    zalgo_num = math.random(1, intensity)
    for i=1, zalgo_num
      zalgoized[#zalgoized + 1] = zalgo_chars[math.random(1, #zalgo_chars)]
  table.concat(zalgoized)

PRIVMSG:
  '^%pwide (.+)$': (source, destination, arg) =>
    say wireplace(0xFEE0, arg)
  '^%pblackletter (.+)$': (source, destination, arg) =>
    say remap(maps.bl, arg)
  '^%pcircled (.+)$': (source, destination, arg) =>
    say remap(maps.ci, arg)
  '^%pzalgo (.+)$': (source, destination, arg) =>
    say zalgo(arg, 7)
  '^%pupsidedown (.+)$': (source, destination, arg) =>
    say remap(maps.ud, util.utf8.reverse(arg))
  '^%pflip (.+)$': (source, destination, arg) =>
    say remap(maps.ud, arg)
  '^%pthrow (.+)$': (source, destination, arg) =>
    say "（╯°□°）╯︵ #{remap maps.ud, util.utf8.reverse(arg)}"
  '^%pparanthesized (.+)$': (source, destination, arg) =>
    say remap(maps.pt, arg)
  '^%pnegcircle (.+)$': (source, destination, arg) =>
    say remap(maps['nc'], arg)
  '^%psquare (.+)$': (source, destination, arg) =>
    say remap(maps.sq, arg)
  '^%pnegsquare (.+)$': (source, destination, arg) =>
    say remap(maps.ns, arg)
  '^%pdoublestruck (.+)$': (source, destination, arg) =>
    say remap(maps.ds, arg)
  '^%pubold (.+)$': (source, destination, arg) =>
    say remap(maps.bo, arg)
  '^%pbolditalic (.+)$': (source, destination, arg) =>
    say remap(maps.bi, arg)
  '^%pboldscript (.+)$': (source, destination, arg) =>
    say remap(maps.bs, arg)
  '^%putfuk (.+)$': (source, destination, arg) =>
    keys = [x for x,_ in pairs(maps)]
    say table.concat([remap(maps[keys[math.random(#keys)]], letter) for letter in codepoints(arg)])

html2unicode = require'html'
math = require'math'

an = [[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789,.?!"'`()[]{}<>&_]]
charmaps = {
ci: 'ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ⓪①②③④⑤⑥⑦⑧⑨'
bl: '𝔄𝔅ℭ𝔇𝔈𝔉𝔊ℌℑ𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔ℜ𝔖𝔗𝔘𝔙𝔚𝔛𝔜ℨ𝔞𝔟𝔠𝔡𝔢𝔣𝔤𝔥𝔦𝔧𝔨𝔩𝔪𝔫𝔬𝔭𝔮𝔯𝔰𝔱𝔲𝔳𝔴𝔵𝔶𝔷'
ud: [[∀BƆDƎℲפHIſK˥WNOԀQRS┴∩ΛMX⅄Zɐqɔpǝɟƃɥᴉɾʞlɯuodbɹsʇnʌʍxʎz0ƖᄅƐㄣϛ9ㄥ86'˙¿¡,,,)(][}{><⅋‾]]
nc: [[🅐🅑🅒🅓🅔🅕🅖🅗🅘🅙🅚🅛🅜🅝🅞🅟🅠🅡🅢🅣🅤🅥🅦🅧🅨🅩🅐🅑🅒🅓🅔🅕🅖🅗🅘🅙🅚🅛🅜🅝🅞🅟🅠🅡🅢🅣🅤🅥🅦🅧🅨🅩⓿]]
sq: [[🄰🄱🄲🄳🄴🄵🄶🄷🄸🄹🄺🄻🄼🄽🄾🄿🅀🅁🅂🅃🅄🅅🅆🅇🅈🅉🄰🄱🄲🄳🄴🄵🄶🄷🄸🄹🄺🄻🄼🄽🄾🄿🅀🅁🅂🅃🅄🅅🅆🅇🅈🅉0123456789,⊡]]
ns: [[🅰🅱🅲🅳🅴🅵🅶🅷🅸🅹🅺🅻🅼🅽🅾🅿🆀🆁🆂🆃🆄🆅🆆🆇🆈🆉🅰🅱🅲🅳🅴🅵🅶🅷🅸🅹🅺🅻🅼🅽🅾🅿🆀🆁🆂🆃🆄🆅🆆🆇🆈🆉]]
ds: [[𝔸𝔹ℂ𝔻𝔼𝔽𝔾ℍ𝕀𝕁𝕂𝕃𝕄ℕ𝕆ℙℚℝ𝕊𝕋𝕌𝕍𝕎𝕏𝕐ℤ𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡]]
bo: [[𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗]]
bi: [[𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁𝒂𝒃𝒄𝒅𝒆𝒇𝒈𝒉𝒊𝒋𝒌𝒍𝒎𝒏𝒐𝒑𝒒𝒓𝒔𝒕𝒖𝒗𝒘𝒙𝒚𝒛0123456789]]
bs: [[𝓐𝓑𝓒𝓓𝓔𝓕𝓖𝓗𝓘𝓙𝓚𝓛𝓜𝓝𝓞𝓟𝓠𝓡𝓢𝓣𝓤𝓥𝓦𝓧𝓨𝓩𝓪𝓫𝓬𝓭𝓮𝓯𝓰𝓱𝓲𝓳𝓴𝓵𝓶𝓷𝓸𝓹𝓺𝓻𝓼𝓽𝓾𝓿𝔀𝔁𝔂𝔃]]
pt: [[⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵0⑴⑵⑶⑷⑸⑹⑺⑻⑼]]
}
codepoints = (str) ->
  str\gmatch("[%z\1-\127\194-\244][\128-\191]*")

-- Construct a table which can be used for lookup replacement later
-- Iterate over normal ascii and find counterparts in the weirdo strings as substrings
maps = {}
for charmap, chars in pairs charmaps
  i = 1
  maps[charmap] = {}
  for uchar in string.gmatch(chars, "([%z\1-\127\194-\244][\128-\191]*)")
    maps[charmap][an\sub(i,i)] = uchar
    i = i +1

unichr = (n) ->
  html2unicode('&#x%x;'\format(n))

wireplace = (offset, arg) ->
    s = arg or ''
    t = {}
    for i = 1, #s
      bc = string.byte(s, i, i)
      -- Replace space width ideographic space for fullwidth offset
      if bc == 32 and offset == 0xFEE0
        t[#t + 1] = '\227\128\128'
      elseif bc == 32
        t[#t + 1] = ' '
      elseif bc < 0x80 then
        t[#t + 1] = html2unicode("&#" .. (offset + bc) .. ";")
      else
        t[#t + 1] = s\sub(i, i)

    table.concat(t, "")

remap = (map, s) ->
  table.concat [map[s\sub(i,i)] or s\sub(i,i) for i=1, #s], ''

zalgo = (text, intensity=50) ->
  zalgo_chars = {}
  for i=0x0300, 0x036f
    zalgo_chars[i-0x2ff] = unichr(i)

  zalgo_chars[#zalgo_chars + 1] = unichr(0x0488)
  zalgo_chars[#zalgo_chars + 0] = unichr(0x0489)

  zalgoized = {}
  for letter in codepoints(text)
    zalgoized[#zalgoized + 1] = letter
    zalgo_num = math.random(1, intensity)
    for i=1, zalgo_num
      zalgoized[#zalgoized + 1] = zalgo_chars[math.random(1, #zalgo_chars)]
  table.concat(zalgoized)

PRIVMSG:
  '^%pwide (.+)$': (source, destination, arg) =>
    say wireplace(0xFEE0, arg)
  '^%pblackletter (.+)$': (source, destination, arg) =>
    say remap(maps.bl, arg)
  '^%pcircled (.+)$': (source, destination, arg) =>
    say remap(maps.ci, arg)
  '^%pzalgo (.+)$': (source, destination, arg) =>
    say zalgo(arg, 7)
  '^%pupsidedown (.+)$': (source, destination, arg) =>
    say remap(maps.ud, arg)
  '^%pflip (.+)$': (source, destination, arg) =>
    say remap(maps.ud, arg)
  '^%pthrow (.+)$': (source, destination, arg) =>
    say "（╯°□°）╯︵ #{remap maps.ud, arg}"
  '^%pparanthesized (.+)$': (source, destination, arg) =>
    say remap(maps.pt, arg)
  '^%pnegcircle (.+)$': (source, destination, arg) =>
    say remap(maps['nc'], arg)
  '^%psquare (.+)$': (source, destination, arg) =>
    say remap(maps.sq, arg)
  '^%pnegsquare (.+)$': (source, destination, arg) =>
    say remap(maps.ns, arg)
  '^%pdoublestruck (.+)$': (source, destination, arg) =>
    say remap(maps.ds, arg)
  '^%pubold (.+)$': (source, destination, arg) =>
    say remap(maps.bo, arg)
  '^%pbolditalic (.+)$': (source, destination, arg) =>
    say remap(maps.bi, arg)
  '^%pboldscript (.+)$': (source, destination, arg) =>
    say remap(maps.bs, arg)
  '^%putfuk (.+)$': (source, destination, arg) =>
    keys = [x for x,_ in pairs(maps)]
    say table.concat([remap(maps[keys[math.random(#keys)]], letter) for letter in codepoints(arg)])


Fixes to legs

Fixes to legs

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

class PonyRenderController
    @AVALIABLE_CONTROLLERS = {}
    @MODELS = {'models/ppm/player_default_base.mdl', 'models/ppm/player_default_base_nj.mdl', 'models/cppm/player_default_base.mdl', 'models/cppm/player_default_base_nj.mdl'}
    @__inherited: (child) =>
        child.MODELS_HASH = {mod, true for mod in *child.MODELS}
        @AVALIABLE_CONTROLLERS[mod] = child for mod in *child.MODELS
    @__inherited(@)

    CompileTextures: => @GetTextureController()\CompileTextures()
    new: (data) =>
        @networkedData = data
        @ent = data.ent
        @modelCached = data\GetModel()
        @CompileTextures()
        @CreateLegs() if @ent == LocalPlayer()
    GetEntity: => @ent
    GetData: => @networkedData
    GetModel: => @networkedData\GetModel()

    GetLegs: =>
        return NULL if @ent ~= LocalPlayer()
        @CreateLegs() if not IsValid()
        return @legsModel
    CreateLegs: =>
        for ent in *ents.GetAll()
            if ent.isPonyLegsModel
                ent\Remove()

        return NULL if @ent ~= LocalPlayer()
        @legsModel = ClientsideModel(@modelCached)
        with @legsModel
            .isPonyLegsModel = true
            \SetNoDraw(true)
            .__PPM2_PonyData = @GetData()

        @lastLegUpdate = CurTime()
        @legClipPlanePos = Vector(0, 0, 0)
        @legBGSetup = CurTime()
        @legUpdateFrame = 0
        @legClipDot = 0
        @duckOffsetHack = @@LEG_CLIP_OFFSET_STAND
        @legsClipPlane = @@LEG_CLIP_VECTOR
        return @legsModel

    @LEG_SHIFT_CONST = 24
    @LEG_SHIFT_CONST_VEHICLE = 14
    @LEG_Z_CONST = 0
    @LEG_Z_CONST_VEHICLE = 20
    @LEG_ANIM_SPEED_CONST = 2
    @LEG_CLIP_OFFSET_STAND = 28
    @LEG_CLIP_OFFSET_DUCK = 12
    @LEG_CLIP_OFFSET_VEHICLE = 11

    UpdateLegs: =>
        return unless IsValid(@legsModel)
        return if @legUpdateFrame == FrameNumber()
        @legUpdateFrame = FrameNumber()
        ctime = CurTime()
        ply = @ent
        seq = ply\GetSequence()

        if seq ~= @legSeq
            @legSeq = seq
            @legsModel\ResetSequence(seq)

        if @legBGSetup < ctime
            @legBGSetup = ctime + 1
            for group in *ply\GetBodyGroups()
                @legsModel\SetBodygroup(group.id, ply\GetBodygroup(group.id))

        with @legsModel
            \FrameAdvance(ctime - @lastLegUpdate)
            \SetPlaybackRate(@@LEG_ANIM_SPEED_CONST)
            @lastLegUpdate = ctime
            \SetPoseParameter('move_x',       (ply\GetPoseParameter('move_x')     * 2) - 1)
            \SetPoseParameter('move_y',       (ply\GetPoseParameter('move_y')     * 2) - 1)
            \SetPoseParameter('move_yaw',     (ply\GetPoseParameter('move_yaw')   * 360) - 180)
            \SetPoseParameter('body_yaw',     (ply\GetPoseParameter('body_yaw')   * 180) - 90)
            \SetPoseParameter('spine_yaw',    (ply\GetPoseParameter('spine_yaw')  * 180) - 90)

        if ply\InVehicle()
            veh = ply\GetVehicle()
            vehAng = veh\GetAngles()
            eyepos = EyePos()
            vehAng\RotateAroundAxis(vehAng\Up(), 90)

            clipAng = Angle(vehAng.p, vehAng.y, vehAng.r)
            clipAng\RotateAroundAxis(clipAng\Right(), -90)

            @legsClipPlane = clipAng\Forward()
            @legsModel\SetRenderAngles(vehAng)

            legClipPlanePos = Vector(0, 0, @@LEG_CLIP_OFFSET_VEHICLE)
            legClipPlanePos\Rotate(vehAng)
            @legClipPlanePos = eyepos - legClipPlanePos

            drawPos = Vector(@@LEG_SHIFT_CONST_VEHICLE, 0, @@LEG_Z_CONST_VEHICLE)
            drawPos\Rotate(vehAng)
            @legsModel\SetRenderOrigin(eyepos - drawPos)
        else
            @legsClipPlane = @@LEG_CLIP_VECTOR
            eangles = EyeAngles()
            yaw = eangles.y - ply\GetPoseParameter('head_yaw') * 180 + 90
            newAng = Angle(0, yaw, 0)
            rad = math.rad(yaw)
            sin, cos = math.sin(rad), math.cos(rad)
            pos = ply\GetPos()
            {:x, :y, :z} = pos
            newPos = Vector(x - cos * @@LEG_SHIFT_CONST, y - sin * @@LEG_SHIFT_CONST, z + @@LEG_Z_CONST)
            if ply\Crouching()
                @duckOffsetHack = @@LEG_CLIP_OFFSET_DUCK
            else
                @duckOffsetHack = Lerp(0.1, @duckOffsetHack, @@LEG_CLIP_OFFSET_STAND)

            @legClipPlanePos = Vector(x, y, z + @duckOffsetHack)
            @legsModel\SetRenderAngles(newAng)
            @legsModel\SetRenderOrigin(newPos)
        @legClipDot = @legsClipPlane\Dot(@legClipPlanePos)

    @LEG_CLIP_VECTOR = Vector(0, 0, -1)
    DrawLegs: (start3D = true) =>
        @CreateLegs() unless IsValid(@legsModel)
        return unless IsValid(@legsModel)
        return if @ent\ShouldDrawLocalPlayer()
        @UpdateLegs()

        oldClip = render.EnableClipping(true)
        render.PushCustomClipPlane(@legsClipPlane, @legClipDot)
        cam.Start3D() if start3D

        @PreDraw()
        @legsModel\DrawModel()
        @PostDraw()

        render.PopCustomClipPlane()
        cam.End3D() if start3D
        render.EnableClipping(oldClip)

    PreDraw: (ent = @ent) =>
        @GetTextureController()\PreDraw(ent)
    PostDraw: (ent = @ent) =>
        @GetTextureController()\PostDraw(ent)

    DataChanges: (state) =>
        return if not @ent
        @GetTextureController()\DataChanges(state)
    GetTextureController: =>
        if not @renderController
            cls = PPM2.GetTextureController(@modelCached)
            @renderController = cls(@)
        @renderController.ent = @ent
        return @renderController

PPM2.PonyRenderController = PonyRenderController
PPM2.GetPonyRenderController = (model = 'models/ppm/player_default_base.mdl') -> PonyRenderController.AVALIABLE_CONTROLLERS[model\lower()] or PonyRenderController
PPM2.GetPonyRendererController = PPM2.GetPonyRenderController
PPM2.GetRenderController = PPM2.GetPonyRenderController
PPM2.GetRendererController = PPM2.GetPonyRenderController

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

class PonyRenderController
    @AVALIABLE_CONTROLLERS = {}
    @MODELS = {'models/ppm/player_default_base.mdl', 'models/ppm/player_default_base_nj.mdl', 'models/cppm/player_default_base.mdl', 'models/cppm/player_default_base_nj.mdl'}
    @__inherited: (child) =>
        child.MODELS_HASH = {mod, true for mod in *child.MODELS}
        @AVALIABLE_CONTROLLERS[mod] = child for mod in *child.MODELS
    @__inherited(@)

    CompileTextures: => @GetTextureController()\CompileTextures()
    new: (data) =>
        @networkedData = data
        @ent = data.ent
        @modelCached = data\GetModel()
        @CompileTextures()
        @CreateLegs() if @ent == LocalPlayer()
    GetEntity: => @ent
    GetData: => @networkedData
    GetModel: => @networkedData\GetModel()

    GetLegs: =>
        return NULL if @ent ~= LocalPlayer()
        @CreateLegs() if not IsValid()
        return @legsModel
    CreateLegs: =>
        for ent in *ents.GetAll()
            if ent.isPonyLegsModel
                ent\Remove()

        return NULL if @ent ~= LocalPlayer()
        @legsModel = ClientsideModel(@modelCached)
        with @legsModel
            .isPonyLegsModel = true
            \SetNoDraw(true)
            .__PPM2_PonyData = @GetData()

        @lastLegUpdate = CurTime()
        @legClipPlanePos = Vector(0, 0, 0)
        @legBGSetup = CurTime()
        @legUpdateFrame = 0
        @legClipDot = 0
        @duckOffsetHack = @@LEG_CLIP_OFFSET_STAND
        return @legsModel

    @LEG_SHIFT_CONST = 24
    @LEG_ANIM_SPEED_CONST = 2
    @LEG_CLIP_OFFSET_STAND = 28
    @LEG_CLIP_OFFSET_DUCK = 12
    @LEG_CLIP_OFFSET_VEHICLE = 20

    UpdateLegs: =>
        return unless IsValid(@legsModel)
        return if @legUpdateFrame == FrameNumber()
        @legUpdateFrame = FrameNumber()
        ctime = CurTime()
        ply = @ent
        seq = ply\GetSequence()

        if seq ~= @legSeq
            @legSeq = seq
            @legsModel\ResetSequence(seq)

        if @legBGSetup < ctime
            @legBGSetup = ctime + 1
            for group in *ply\GetBodyGroups()
                @legsModel\SetBodygroup(group.id, ply\GetBodygroup(group.id))

        with @legsModel
            \FrameAdvance(ctime - @lastLegUpdate)
            \SetPlaybackRate(@@LEG_ANIM_SPEED_CONST)
            @lastLegUpdate = ctime
            \SetPoseParameter('move_x',       (ply\GetPoseParameter('move_x')     * 2) - 1)
            \SetPoseParameter('move_y',       (ply\GetPoseParameter('move_y')     * 2) - 1)
            \SetPoseParameter('move_yaw',     (ply\GetPoseParameter('move_yaw')   * 360) - 180)
            \SetPoseParameter('body_yaw',     (ply\GetPoseParameter('body_yaw')   * 180) - 90)
            \SetPoseParameter('spine_yaw',    (ply\GetPoseParameter('spine_yaw')  * 180) - 90)

        if ply\InVehicle()
            veh = ply\GetVehicle()
            vehAng = veh\GetAngles()
            vehPos = veh\GetPos()
            vehAng\RotateAroundAxis(vehAng\Up(), 90)
            @legsModel\SetRenderAngles(vehAng)
            @legsModel\SetRenderOrigin(vehPos)
            {:x, :y, :z} = vehPos
            @legClipPlanePos = Vector(x, y, z + @@LEG_CLIP_OFFSET_VEHICLE)
        else
            eangles = EyeAngles()
            yaw = eangles.y - ply\GetPoseParameter('head_yaw') * 180 + 90
            newAng = Angle(0, yaw, 0)
            rad = math.rad(yaw)
            sin, cos = math.sin(rad), math.cos(rad)
            pos = ply\GetPos()
            {:x, :y, :z} = pos
            newPos = Vector(x - cos * @@LEG_SHIFT_CONST, y - sin * @@LEG_SHIFT_CONST, z)
            if ply\Crouching()
                @duckOffsetHack = @@LEG_CLIP_OFFSET_DUCK
            else
                @duckOffsetHack = Lerp(0.1, @duckOffsetHack, @@LEG_CLIP_OFFSET_STAND)

            @legClipPlanePos = Vector(x, y, z + @duckOffsetHack)
            @legsModel\SetRenderAngles(newAng)
            @legsModel\SetRenderOrigin(newPos)
        @legClipDot = @@LEG_CLIP_VECTOR\Dot(@legClipPlanePos)

    @LEG_CLIP_VECTOR = Vector(0, 0, -1)
    DrawLegs: (start3D = true) =>
        @CreateLegs() unless IsValid(@legsModel)
        return unless IsValid(@legsModel)
        return if @ent\ShouldDrawLocalPlayer()
        @UpdateLegs()

        oldClip = render.EnableClipping(true)
        render.PushCustomClipPlane(@@LEG_CLIP_VECTOR, @legClipDot)
        cam.Start3D() if start3D

        @PreDraw()
        @legsModel\DrawModel()
        @PostDraw()

        render.PopCustomClipPlane()
        cam.End3D() if start3D
        render.EnableClipping(oldClip)

    PreDraw: (ent = @ent) =>
        @GetTextureController()\PreDraw(ent)
    PostDraw: (ent = @ent) =>
        @GetTextureController()\PostDraw(ent)

    DataChanges: (state) =>
        return if not @ent
        @GetTextureController()\DataChanges(state)
    GetTextureController: =>
        if not @renderController
            cls = PPM2.GetTextureController(@modelCached)
            @renderController = cls(@)
        @renderController.ent = @ent
        return @renderController

PPM2.PonyRenderController = PonyRenderController
PPM2.GetPonyRenderController = (model = 'models/ppm/player_default_base.mdl') -> PonyRenderController.AVALIABLE_CONTROLLERS[model\lower()] or PonyRenderController
PPM2.GetPonyRendererController = PPM2.GetPonyRenderController
PPM2.GetRenderController = PPM2.GetPonyRenderController
PPM2.GetRendererController = PPM2.GetPonyRenderController


Refactored variables

Refactored variables

--
-- 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 def, 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 def, 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'

for key, value in pairs PPM2.PonyDataRegistry
    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

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

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

FLOAT_FIXER = (def = 1, min = 0, max = 1) ->
    return (arg = def) -> math.Clamp(tonumber(arg) or def, min, max)

PPM2.PonyDataRegistry = {
    'age': {
        default: -> PPM2.AGE_ADULT
        getFunc: 'Age'
        enum: {'FILLY', 'ADULT', 'MATURE'}
        fix: (arg = PPM2.AGE_ADULT) -> math.Clamp(tonumber(arg) or PPM2.AGE_ADULT, 0, 2)
        min: 0
        max: 2
    }

    'race': {
        default: -> PPM2.RACE_EARTH
        getFunc: 'Race'
        enum: [arg for arg in *PPM2.RACE_ENUMS]
        fix: (arg = PPM2.RACE_EARTH) -> math.Clamp(tonumber(arg) or PPM2.RACE_EARTH, 0, 3)
        min: 0
        max: 3
    }

    'wings_type': {
        default: -> 0
        getFunc: 'WingsType'
        enum: [arg for arg in *PPM2.AvaliablePonyWings]
        fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_WINGS, PPM2.MAX_WINGS)
        min: PPM2.MIN_WINGS
        max: PPM2.MAX_WINGS
    }

    'gender': {
        default: -> PPM2.GENDER_FEMALE
        getFunc: 'Gender'
        enum: [arg for arg in *PPM2.AGE_ENUMS]
        fix: (arg = PPM2.GENDER_FEMALE) -> math.Clamp(tonumber(arg) or PPM2.GENDER_FEMALE, 0, 1)
        min: 0
        max: 1
    }

    'weight': {
        default: -> 1
        getFunc: 'Weight'
        fix: (arg = 1) -> math.Clamp(tonumber(arg) or 1, PPM2.MIN_WEIGHT, PPM2.MAX_WEIGHT)
        min: PPM2.MIN_WEIGHT
        max: PPM2.MAX_WEIGHT
    }

    'ponysize': {
        default: -> 1
        getFunc: 'PonySize'
        fix: (arg = 1) -> math.Clamp(tonumber(arg) or 1, PPM2.MIN_SCALE, PPM2.MAX_SCALE)
        min: PPM2.MIN_SCALE
        max: PPM2.MAX_SCALE
    }

    'necksize': {
        default: -> 1
        getFunc: 'NeckSize'
        fix: (arg = 1) -> math.Clamp(tonumber(arg) or 1, PPM2.MIN_NECK, PPM2.MAX_NECK)
        min: PPM2.MIN_NECK
        max: PPM2.MAX_NECK
    }

    'legssize': {
        default: -> 1
        getFunc: 'LegsSize'
        fix: (arg = 1) -> math.Clamp(tonumber(arg) or 1, PPM2.MIN_LEGS, PPM2.MAX_LEGS)
        min: PPM2.MIN_LEGS
        max: PPM2.MAX_LEGS
    }

    'male_buff': {
        default: -> PPM2.DEFAULT_MALE_BUFF
        getFunc: 'MaleBuff'
        fix: (arg = PPM2.DEFAULT_MALE_BUFF) -> math.Clamp(tonumber(arg) or PPM2.DEFAULT_MALE_BUFF, PPM2.MIN_MALE_BUFF, PPM2.MAX_MALE_BUFF)
        min: PPM2.MIN_MALE_BUFF
        max: PPM2.MAX_MALE_BUFF
    }

    'eyelash': {
        default: -> 0
        getFunc: 'EyelashType'
        enum: [arg for arg in *PPM2.EyelashTypes]
        fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_EYELASHES, PPM2.MAX_EYELASHES)
        min: PPM2.MIN_EYELASHES
        max: PPM2.MAX_EYELASHES
    }

    'tail': {
        default: -> 0
        getFunc: 'TailType'
        enum: [arg for arg in *PPM2.AvaliableTails] -- fast copy
        fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_TAILS, PPM2.MAX_TAILS)
        min: PPM2.MIN_TAILS
        max: PPM2.MAX_TAILS
    }

    'tail_new': {
        default: -> 0
        getFunc: 'TailTypeNew'
        enum: [arg for arg in *PPM2.AvaliableTailsNew] -- fast copy
        fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_TAILS_NEW, PPM2.MAX_TAILS_NEW)
        min: PPM2.MIN_TAILS_NEW
        max: PPM2.MAX_TAILS_NEW
    }

    'mane': {
        default: -> 0
        getFunc: 'ManeType'
        enum: [arg for arg in *PPM2.AvaliableUpperManes] -- fast copy
        fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_UPPER_MANES, PPM2.MAX_UPPER_MANES)
        min: PPM2.MIN_UPPER_MANES
        max: PPM2.MAX_UPPER_MANES
    }

    'mane_new': {
        default: -> 0
        getFunc: 'ManeTypeNew'
        enum: [arg for arg in *PPM2.AvaliableUpperManesNew] -- fast copy
        fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_UPPER_MANES_NEW, PPM2.MAX_UPPER_MANES_NEW)
        min: PPM2.MIN_UPPER_MANES_NEW
        max: PPM2.MAX_UPPER_MANES_NEW
    }

    'manelower': {
        default: -> 0
        getFunc: 'ManeTypeLower'
        enum: [arg for arg in *PPM2.AvaliableLowerManes] -- fast copy
        fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_LOWER_MANES, PPM2.MAX_LOWER_MANES)
        min: PPM2.MIN_LOWER_MANES
        max: PPM2.MAX_LOWER_MANES
    }

    'manelower_new': {
        default: -> 0
        getFunc: 'ManeTypeLowerNew'
        enum: [arg for arg in *PPM2.AvaliableLowerManesNew] -- fast copy
        fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_LOWER_MANES_NEW, PPM2.MAX_LOWER_MANES_NEW)
        min: PPM2.MIN_LOWER_MANES_NEW
        max: PPM2.MAX_LOWER_MANES_NEW
    }

    'socks_texture': {
        default: -> 0
        getFunc: 'SocksTexture'
        enum: [arg for arg in *PPM2.SocksTypes] -- fast copy
        fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_SOCKS, PPM2.MAX_SOCKS)
        min: PPM2.MIN_SOCKS
        max: PPM2.MAX_SOCKS
    }

    'socks_texture_url': {
        default: -> ''
        getFunc: 'SocksTextureURL'
        fix: URL_FIXER
    }

    'tailsize': {
        default: -> 1
        getFunc: 'TailSize'
        fix: (arg = 1) -> math.Clamp(tonumber(arg) or 1, PPM2.MIN_TAIL_SIZE, PPM2.MAX_TAIL_SIZE)
        min: PPM2.MIN_TAIL_SIZE
        max: PPM2.MAX_TAIL_SIZE
    }

    'cmark': {
        default: -> true
        getFunc: 'CMark'
        fix: (arg = true) -> tobool(arg)
    }

    'cmark_size': {
        default: -> 1
        getFunc: 'CMarkSize'
        fix: (arg = 1) -> math.Clamp(tonumber(arg) or 1, 0, 1)
        min: 0
        max: 1
    }

    'cmark_color': {
        default: -> Color(255, 255, 255)
        getFunc: 'CMarkColor'
        fix: COLOR_FIXER()
    }

    'fangs': {
        default: -> false
        getFunc: 'Fangs'
        fix: (arg = false) -> tobool(arg)
    }

    'bat_pony_ears': {
        default: -> false
        getFunc: 'BatPonyEars'
        fix: (arg = false) -> tobool(arg)
    }

    'claw_teeth': {
        default: -> false
        getFunc: 'ClawTeeth'
        fix: (arg = false) -> tobool(arg)
    }

    'cmark_type': {
        default: -> 4
        getFunc: 'CMarkType'
        enum: [arg for arg in *PPM2.DefaultCutiemarks]
        fix: (arg = 4) -> math.Clamp(tonumber(arg) or 4, PPM2.MIN_CMARK, PPM2.MAX_CMARK)
        min: PPM2.MIN_CMARK
        max: PPM2.MAX_CMARK
    }

    'cmark_url': {
        default: -> ''
        getFunc: 'CMarkURL'
        fix: URL_FIXER
    }

    'body': {
        default: -> Color(255, 255, 255)
        getFunc: 'BodyColor'
        fix: COLOR_FIXER()
    }

    'horn_color': {
        default: -> Color(255, 255, 255)
        getFunc: 'HornColor'
        fix: COLOR_FIXER()
    }

    'wings_color': {
        default: -> Color(255, 255, 255)
        getFunc: 'WingsColor'
        fix: COLOR_FIXER()
    }

    'separate_wings': {
        default: -> false
        getFunc: 'SeparateWings'
        fix: (arg = false) -> tobool(arg)
    }

    'separate_horn': {
        default: -> false
        getFunc: 'SeparateHorn'
        fix: (arg = false) -> tobool(arg)
    }

    'use_horn_detail': {
        default: -> false
        getFunc: 'UseHornDetail'
        fix: (arg = false) -> tobool(arg)
    }

    'horn_glow': {
        default: -> false
        getFunc: 'HornGlow'
        fix: (arg = false) -> tobool(arg)
    }

    'horn_glow_strength': {
        default: -> 1
        getFunc: 'HornGlowSrength'
        fix: (arg = 1) -> math.Clamp(tonumber(arg) or 0, 0, 1)
        min: 0
        max: 1
    }

    'horn_detail_color': {
        default: -> Color(255, 255, 255)
        getFunc: 'HornDetailColor'
        fix: COLOR_FIXER()
    }

    'separate_eyes': {
        default: -> false
        getFunc: 'SeparateEyes'
        fix: (arg = false) -> tobool(arg)
    }

    'separate_mane': {
        default: -> false
        getFunc: 'SeparateMane'
        fix: (arg = false) -> tobool(arg)
    }

    'socks': {
        default: -> false
        getFunc: 'Socks'
        fix: (arg = false) -> tobool(arg)
    }

    'new_male_muzzle': {
        default: -> true
        getFunc: 'NewMuzzle'
        fix: (arg = true) -> tobool(arg)
    }

    'noflex': {
        default: -> false
        getFunc: 'NoFlex'
        fix: (arg = false) -> tobool(arg)
    }

    'socks_model': {
        default: -> false
        getFunc: 'SocksAsModel'
        fix: (arg = false) -> tobool(arg)
    }

    'socks_model_color': {
        default: -> Color(255, 255, 255)
        getFunc: 'SocksColor'
        fix: COLOR_FIXER()
    }

    'suit': {
        default: -> 0
        getFunc: 'Bodysuit'
        enum: [arg for arg in *PPM2.AvaliablePonySuits]
        fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_SUIT, PPM2.MAX_SUIT)
        min: PPM2.MIN_SUIT
        max: PPM2.MAX_SUIT
    }

    'left_wing_size': {
        default: -> 1
        getFunc: 'LWingSize'
        fix: (arg = 1) -> math.Clamp(tonumber(arg) or 1, PPM2.MIN_WING, PPM2.MAX_WING)
        min: PPM2.MIN_WING
        max: PPM2.MAX_WING
    }

    'left_wing_x': {
        default: -> 0
        getFunc: 'LWingX'
        fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_WINGX, PPM2.MAX_WINGX)
        min: PPM2.MIN_WINGX
        max: PPM2.MAX_WINGX
    }

    'left_wing_y': {
        default: -> 0
        getFunc: 'LWingY'
        fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_WINGY, PPM2.MAX_WINGY)
        min: PPM2.MIN_WINGY
        max: PPM2.MAX_WINGY
    }

    'left_wing_z': {
        default: -> 0
        getFunc: 'LWingZ'
        fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_WINGZ, PPM2.MAX_WINGZ)
        min: PPM2.MIN_WINGZ
        max: PPM2.MAX_WINGZ
    }

    'right_wing_size': {
        default: -> 1
        getFunc: 'RWingSize'
        fix: (arg = 1) -> math.Clamp(tonumber(arg) or 1, PPM2.MIN_WING, PPM2.MAX_WING)
        min: PPM2.MIN_WING
        max: PPM2.MAX_WING
    }

    'right_wing_x': {
        default: -> 0
        getFunc: 'RWingX'
        fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_WINGX, PPM2.MAX_WINGX)
        min: PPM2.MIN_WINGX
        max: PPM2.MAX_WINGX
    }

    'right_wing_y': {
        default: -> 0
        getFunc: 'RWingY'
        fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_WINGY, PPM2.MAX_WINGY)
        min: PPM2.MIN_WINGY
        max: PPM2.MAX_WINGY
    }

    'right_wing_z': {
        default: -> 0
        getFunc: 'RWingZ'
        fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_WINGZ, PPM2.MAX_WINGZ)
        min: PPM2.MIN_WINGZ
        max: PPM2.MAX_WINGZ
    }

    'teeth_color': {
        default: -> Color(255, 255, 255)
        getFunc: 'TeethColor'
        fix: COLOR_FIXER()
    }

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

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

    'bat_wing_color': {
        default: -> Color(255, 255, 255)
        getFunc: 'BatWingColor'
        fix: COLOR_FIXER()
    }

    'bat_wing_skin_color': {
        default: -> Color(255, 255, 255)
        getFunc: 'BatWingSkinColor'
        fix: COLOR_FIXER()
    }

    'separate_horn_phong': {
        default: -> false
        getFunc: 'SeparateHornPhong'
        fix: (arg = false) -> tobool(arg)
    }

    'separate_wings_phong': {
        default: -> false
        getFunc: 'SeparateWingsPhong'
        fix: (arg = false) -> tobool(arg)
    }

    'separate_mane_phong': {
        default: -> false
        getFunc: 'SeparateManePhong'
        fix: (arg = false) -> tobool(arg)
    }

    'separate_tail_phong': {
        default: -> false
        getFunc: 'SeparateTailPhong'
        fix: (arg = false) -> tobool(arg)
    }

    'alternative_fangs': {
        default: -> false
        getFunc: 'AlternativeFangs'
        fix: (arg = false) -> tobool(arg)
    }

    'hoof_fluffers': {
        default: -> false
        getFunc: 'HoofFluffers'
        fix: (arg = false) -> tobool(arg)
    }

    'hoof_fluffers_strength': {
        default: -> 1
        getFunc: 'HoofFluffersStrength'
        fix: FLOAT_FIXER(1)
        min: 0
        max: 1
    }

    'ears_size': {
        default: -> 1
        getFunc: 'EarsSize'
        fix: FLOAT_FIXER(1, 0.1, 2)
        min: 0.1
        max: 2
    }

    'bat_pony_ears_strength': {
        default: -> 1
        getFunc: 'BatPonyEarsStrength'
        fix: FLOAT_FIXER(1)
        min: 0
        max: 1
    }

    'fangs_strength': {
        default: -> 1
        getFunc: 'FangsStrength'
        fix: FLOAT_FIXER(1)
        min: 0
        max: 1
    }

    'clawteeth_strength': {
        default: -> 1
        getFunc: 'ClawTeethStrength'
        fix: FLOAT_FIXER(1)
        min: 0
        max: 1
    }
}

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

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

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

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

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

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

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

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

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

    PPM2.PonyDataRegistry["eye_lines#{internal}"] = {
        default: -> true
        getFunc: "EyeLines#{publicName}"
        fix: (arg = true) -> tobool(arg)
    }

    PPM2.PonyDataRegistry["eye_iris_size#{internal}"] = {
        default: -> 1
        getFunc: "IrisSize#{publicName}"
        fix: (arg = 1) -> math.Clamp(tonumber(arg) or 1, PPM2.MIN_IRIS, PPM2.MAX_IRIS)
        min: PPM2.MIN_IRIS
        max: PPM2.MAX_IRIS
    }

    PPM2.PonyDataRegistry["eye_derp#{internal}"] = {
        default: -> false
        getFunc: "DerpEyes#{publicName}"
        fix: (arg = true) -> tobool(arg)
    }

    PPM2.PonyDataRegistry["eye_derp_strength#{internal}"] = {
        default: -> 1
        getFunc: "DerpEyesStrength#{publicName}"
        fix: (arg = 1) -> math.Clamp(tonumber(arg) or 1, PPM2.MIN_DERP_STRENGTH, PPM2.MAX_DERP_STRENGTH)
        min: PPM2.MIN_DERP_STRENGTH
        max: PPM2.MAX_DERP_STRENGTH
    }

    PPM2.PonyDataRegistry["eye_type#{internal}"] = {
        default: -> 0
        getFunc: "EyeType#{publicName}"
        enum: [arg for arg in *PPM2.AvaliableEyeTypes]
        fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_EYE_TYPE, PPM2.MAX_EYE_TYPE)
        min: PPM2.MIN_EYE_TYPE
        max: PPM2.MAX_EYE_TYPE
    }

    PPM2.PonyDataRegistry["hole_width#{internal}"] = {
        default: -> 1
        getFunc: "HoleWidth#{publicName}"
        fix: (arg = 1) -> math.Clamp(tonumber(arg) or 1, PPM2.MIN_PUPIL_SIZE, PPM2.MAX_PUPIL_SIZE)
        min: PPM2.MIN_PUPIL_SIZE
        max: PPM2.MAX_PUPIL_SIZE
    }

    PPM2.PonyDataRegistry["hole_height#{internal}"] = {
        default: -> 1
        getFunc: "HoleHeight#{publicName}"
        fix: (arg = 1) -> math.Clamp(tonumber(arg) or 1, PPM2.MIN_PUPIL_SIZE, PPM2.MAX_PUPIL_SIZE)
        min: PPM2.MIN_PUPIL_SIZE
        max: PPM2.MAX_PUPIL_SIZE
    }

    PPM2.PonyDataRegistry["iris_width#{internal}"] = {
        default: -> 1
        getFunc: "IrisWidth#{publicName}"
        fix: (arg = 1) -> math.Clamp(tonumber(arg) or 1, PPM2.MIN_PUPIL_SIZE, PPM2.MAX_PUPIL_SIZE)
        min: PPM2.MIN_PUPIL_SIZE
        max: PPM2.MAX_PUPIL_SIZE
    }

    PPM2.PonyDataRegistry["iris_height#{internal}"] = {
        default: -> 1
        getFunc: "IrisHeight#{publicName}"
        fix: (arg = 1) -> math.Clamp(tonumber(arg) or 1, PPM2.MIN_PUPIL_SIZE, PPM2.MAX_PUPIL_SIZE)
        min: PPM2.MIN_PUPIL_SIZE
        max: PPM2.MAX_PUPIL_SIZE
    }

    PPM2.PonyDataRegistry["hole_shiftx#{internal}"] = {
        default: -> 0
        getFunc: "HoleShiftX#{publicName}"
        fix: (arg = 0) -> math.Clamp(tonumber(arg) or 1, PPM2.MIN_HOLE_SHIFT, PPM2.MAX_HOLE_SHIFT)
        min: PPM2.MIN_HOLE_SHIFT
        max: PPM2.MAX_HOLE_SHIFT
    }

    PPM2.PonyDataRegistry["hole_shifty#{internal}"] = {
        default: -> 0
        getFunc: "HoleShiftY#{publicName}"
        fix: (arg = 0) -> math.Clamp(tonumber(arg) or 1, PPM2.MIN_HOLE_SHIFT, PPM2.MAX_HOLE_SHIFT)
        min: PPM2.MIN_HOLE_SHIFT
        max: PPM2.MAX_HOLE_SHIFT
    }

    PPM2.PonyDataRegistry["eye_hole_size#{internal}"] = {
        default: -> .8
        getFunc: "HoleSize#{publicName}"
        fix: (arg = .8) -> math.Clamp(tonumber(arg) or .8, PPM2.MIN_HOLE, PPM2.MAX_HOLE)
        min: PPM2.MIN_HOLE
        max: PPM2.MAX_HOLE
    }

    PPM2.PonyDataRegistry["eye_rotation#{internal}"] = {
        default: -> 0
        getFunc: "EyeRotation#{publicName}"
        fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_EYE_ROTATION, PPM2.MAX_EYE_ROTATION)
        min: PPM2.MIN_EYE_ROTATION
        max: PPM2.MAX_EYE_ROTATION
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    PPM2.PonyDataRegistry["body_detail_#{i}"] = {
        default: -> 0
        getFunc: "BodyDetail#{i}"
        enum: [arg for arg in *PPM2.BodyDetailsEnum]
        fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_DETAIL, PPM2.MAX_DETAIL)
        min: PPM2.MIN_DETAIL
        max: PPM2.MAX_DETAIL
    }

    PPM2.PonyDataRegistry["body_detail_url_#{i}"] = {
        default: -> ''
        getFunc: "BodyDetailURL#{i}"
        fix: (arg = '') ->
            arg = tostring(arg)
            if arg\find('^https?://')
                return arg
            else
                return ''
    }

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

    PPM2.PonyDataRegistry["body_detail_glow_#{i}"] = {
        default: -> false
        getFunc: "BodyDetailGlow#{i}"
        fix: (arg = false) -> tobool(arg)
    }

    PPM2.PonyDataRegistry["body_detail_glow_strength_#{i}"] = {
        default: -> 1
        getFunc: "BodyDetailGlowStrength#{i}"
        fix: FLOAT_FIXER(1, 0, 1)
        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]
        fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, 0, PPM2.MAX_TATTOOS)
        min: 0
        max: PPM2.MAX_TATTOOS
    }

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

    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
    }

    PPM2.PonyDataRegistry["tattoo_rotate_#{i}"] = {
        default: -> 0
        getFunc: "TattooRotate#{i}"
        fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, -180, 180)
        min: -180
        max: 180
    }

    PPM2.PonyDataRegistry["tattoo_scalex_#{i}"] = {
        default: -> 1
        getFunc: "TattooScaleX#{i}"
        fix: (arg = 1) -> math.Clamp(tonumber(arg) or 1, 0, 10)
        min: 0
        max: 10
    }

    PPM2.PonyDataRegistry["tattoo_glow_strength_#{i}"] = {
        default: -> 1
        getFunc: "TattooGlowStrength#{i}"
        fix: (arg = 1) -> math.Clamp(tonumber(arg) or 1, 0, 1)
        min: 0
        max: 1
    }

    PPM2.PonyDataRegistry["tattoo_scaley_#{i}"] = {
        default: -> 1
        getFunc: "TattooScaleY#{i}"
        fix: (arg = 1) -> math.Clamp(tonumber(arg) or 1, 0, 10)
        min: 0
        max: 10
    }

    PPM2.PonyDataRegistry["tattoo_glow_#{i}"] = {
        default: -> false
        getFunc: "TattooGlow#{i}"
        fix: (arg = false) -> tobool(arg)
    }

    PPM2.PonyDataRegistry["tattoo_over_detail_#{i}"] = {
        default: -> false
        getFunc: "TattooOverDetail#{i}"
        fix: (arg = false) -> tobool(arg)
    }

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

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

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

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

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

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

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

    PPM2.PonyDataRegistry[ttype\lower() .. '_lightwarp_texture'] = {
        default: -> 0
        getFunc: ttype .. 'Lightwarp'
        enum: [arg for arg in *PPM2.AvaliableLightwarps]
        fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, 0, PPM2.MAX_LIGHTWARP)
        min: 0
        max: PPM2.MAX_LIGHTWARP
    }

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

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