Fixed invalid call

Fixed invalid call

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

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

    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


Use direct plus operation

Use direct plus operation

--
-- Copyright (C) 2017-2018 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, StrongEntity, PPM2 from _G
import ALTERNATIVE_RENDER from PPM2
import GetPonyData, IsDormant, PPMBonesModifier, IsPony from FindMetaTable('Entity')

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

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

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

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 = {}

	if not ALTERNATIVE_RENDER\GetBool()
		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 ALTERNATIVE_RENDER\GetBool() and 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 TASK_RENDER_TYPE\GetBool()
		for _, task in ipairs PPM2.NetworkedPonyData.RenderTasks
			ent = task.ent
			if IsValid(ent)
				if ent.__cachedIsPony
					ent\SetNoDraw(true)
					ent.__ppm2_task_hit = true
					renderController = task\GetRenderController()
					renderController\PreDraw(ent)
					IN_DRAW = true
					ent\DrawModel()
					IN_DRAW = false
					renderController\PostDraw(ent)
				else
					if ent.__ppm2_task_hit
						ent.__ppm2_task_hit = false
						ent\SetNoDraw(false)
						task\Reset()

	if not ENABLE_NEW_RAGDOLLS\GetBool()
		for _, ply in 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 = ->
	if TASK_RENDER_TYPE\GetBool()
		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\GetEntity()
					ent.__ppm2_oldRenderOverride = ent.RenderOverride
					ent.__ppm2RenderOverride = ->
						renderController = task\GetRenderController()
						renderController\PreDraw(ent, true)
						ent\DrawModel()
						renderController\PostDraw(ent, true)
						ent.__ppm2_oldRenderOverride(ent) if ent.__ppm2_oldRenderOverride
					ent.RenderOverride = ent.__ppm2RenderOverride

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

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

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

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

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

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

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

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

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

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

--
-- Copyright (C) 2017-2018 DBot

-- 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, StrongEntity, PPM2 from _G
import ALTERNATIVE_RENDER from PPM2
import GetPonyData, IsDormant, PPMBonesModifier, IsPony from FindMetaTable('Entity')

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

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

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

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 = {}

	if not ALTERNATIVE_RENDER\GetBool()
		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 ALTERNATIVE_RENDER\GetBool() and 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 TASK_RENDER_TYPE\GetBool()
		for _, task in ipairs PPM2.NetworkedPonyData.RenderTasks
			ent = task.ent
			if IsValid(ent)
				if ent.__cachedIsPony
					ent\SetNoDraw(true)
					ent.__ppm2_task_hit = true
					renderController = task\GetRenderController()
					renderController\PreDraw(ent)
					IN_DRAW = true
					ent\DrawModel()
					IN_DRAW = false
					renderController\PostDraw(ent)
				else
					if ent.__ppm2_task_hit
						ent.__ppm2_task_hit = false
						ent\SetNoDraw(false)
						task\Reset()

	if not ENABLE_NEW_RAGDOLLS\GetBool()
		for _, ply in 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 = ->
	if TASK_RENDER_TYPE\GetBool()
		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\GetEntity()
					ent.__ppm2_oldRenderOverride = ent.RenderOverride
					ent.__ppm2RenderOverride = ->
						renderController = task\GetRenderController()
						renderController\PreDraw(ent, true)
						ent\DrawModel()
						renderController\PostDraw(ent, true)
						ent.__ppm2_oldRenderOverride(ent) if ent.__ppm2_oldRenderOverride
					ent.RenderOverride = ent.__ppm2RenderOverride

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

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

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

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

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

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

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

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

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

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


utf8: fix zalgorithm

utf8: fix zalgorithm

Former-commit-id: 5eac2fd45103c10c9f92c179b32fad2ce708f27a [formerly a66369abe1d5192cc958877c8f9069df3e3f19ac]
Former-commit-id: 51a3b6960bb169839969f28e7a48854b29f44f2b

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)

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_threshold = intensity
  zalgo_chars = {}
  for i=0x0300, 0x036
    zalgo_chars[i-0x2ff] = unichr(i)

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

  source = text\upper()
  --source = _insert_randoms(source)

  zalgoized = {}
  for letter in codepoints(source)
    zalgoized[#zalgoized + 1] = letter
    zalgo_num = math.random(1, zalgo_threshold)
    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)


translate: new api to replace google that doesn't work with "robots" anymore

-- google translate
util = require'util'
json = util.json
simplehttp = util.simplehttp

languages =
  "af": "Afrikaans",
  "sq": "Albanian",
  "ar": "Arabic",
  "az": "Azerbaijani",
  "eu": "Basque",
  "bn": "Bengali",
  "be": "Belarusian",
  "bg": "Bulgarian",
  "ca": "Catalan",
  "zh-CN": "Simplified Chinese",
  "zh-TW": "Traditional Chinese",
  "hr": "Croatian",
  "cs": "Czech",
  "da": "Danish",
  "nl": "Dutch",
  "en": "English",
  "eo": "Esperanto",
  "et": "Estonian",
  "tl": "Filipino",
  "fi": "Finnish",
  "fr": "French",
  "gl": "Galician",
  "ka": "Georgian",
  "de": "German",
  "el": "Greek",
  "gu": "Gujarati",
  "ht": "Haitian Creole",
  "iw": "Hebrew",
  "hi": "Hindi",
  "hu": "Hungarian",
  "is": "Icelandic",
  "id": "Indonesian",
  "ga": "Irish",
  "it": "Italian",
  "ja": "Japanese",
  "kn": "Kannada",
  "ko": "Korean",
  "la": "Latin",
  "lv": "Latvian",
  "lt": "Lithuanian",
  "mk": "Macedonian",
  "ms": "Malay",
  "mt": "Maltese",
  "no": "Norwegian",
  "fa": "Persian",
  "pl": "Polish",
  "pt": "Portuguese",
  "ro": "Romanian",
  "ru": "Russian",
  "sr": "Serbian",
  "sk": "Slovak",
  "sl": "Slovenian",
  "es": "Spanish",
  "sw": "Swahili",
  "sv": "Swedish",
  "ta": "Tamil",
  "te": "Telugu",
  "th": "Thai",
  "tr": "Turkish",
  "uk": "Ukrainian",
  "ur": "Urdu",
  "vi": "Vietnamese",
  "cy": "Welsh",
  "yi": "Yiddish"

getCode = (language) ->
  for code, lang in pairs languages
    if lang\lower! == language\lower!
      return code
    else if language\lower! == code\lower!
      return code

urlEncode = (str, space) ->
  space = space or '+'
  str = tostring(str)

  str = str\gsub '([^%w ])', (c) ->
    string.format  "%%%02X", string.byte(c)
  return str\gsub(' ', space)

buildQueryStr = (tbl) ->
  out = {}
  for k,v in pairs tbl
    table.insert out, k..'='..urlEncode(v)
  table.concat out, '&'

google_translate = (origin, target, term, cb) ->
  origin = getCode(origin) or 'auto'
  target = getCode(target) or 'en'
  args =
    client: 'p'
    hl: 'en'
    multires: 1
    sc: 1
    sl: origin
    ssel: 0
    tl: target
    tsel: 0
    uptl: "en"
    text: term

  simplehttp {
    url:"http://translate.google.com/translate_a/t?" .. buildQueryStr(args)
    headers: {
      "DNT": "1"
      "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:41.0) Gecko/20100101 Firefox/41.0"
      "Accept-Charset": "UTF-8"
      "Accept-Language":"nn-NO,nn;q=0.9,no-NO;q=0.8,no;q=0.6,nb-NO;q=0.5,nb;q=0.4,en-US;q=0.3,en;q=0.1"
    }
  }, (data) ->
    if #data > 4
      parsed = json.decode data
      language = parsed.src
      out = table.concat [k.trans for k in *parsed.sentences]
      if #out > 0
        cb(out)

translate = (origin, target, term, cb) ->
  origin = getCode(origin) or 'auto'
  target = getCode(target) or 'en'
  args =
    q: term
    langpair: "#{origin}|#{target}"
  simplehttp {
    url:"http://mymemory.translated.net/api/get?" .. buildQueryStr(args)
  }, (data) ->
    parsed = json.decode data
    cb parsed.responseData.translatedText

PRIVMSG:
  '^%ptranslate ([%w%-]+) ([%w%-]+) (.*)$': (source, destination, origin, target, term) =>
    translate origin, target, term, (text) ->
      say text
  '^%ptr ([%w%-]+) ([%w%-]+) (.*)$': (source, destination, origin, target, term) =>
    translate origin, target, term, (text) ->
      say text
  '^%ptlolno (.*)$': (source, destination, text) =>
    translate 'auto', 'zh-TW', text, (text) ->
      translate 'zh-TW', 'no', text, (text) ->
        say text
  '^%ptlol (.*)$': (source, destination, text) =>
    translate 'auto', 'zh-CN', text, (text) ->
      translate 'zh-CN', 'no', text, (text) ->
        translate 'no', 'ja', text, (text) ->
          translate 'ja', 'en', text, (text) ->
            say text

-- google translate
util = require'util'
json = util.json
simplehttp = util.simplehttp

languages =
  "af": "Afrikaans",
  "sq": "Albanian",
  "ar": "Arabic",
  "az": "Azerbaijani",
  "eu": "Basque",
  "bn": "Bengali",
  "be": "Belarusian",
  "bg": "Bulgarian",
  "ca": "Catalan",
  "zh-CN": "Simplified Chinese",
  "zh-TW": "Traditional Chinese",
  "hr": "Croatian",
  "cs": "Czech",
  "da": "Danish",
  "nl": "Dutch",
  "en": "English",
  "eo": "Esperanto",
  "et": "Estonian",
  "tl": "Filipino",
  "fi": "Finnish",
  "fr": "French",
  "gl": "Galician",
  "ka": "Georgian",
  "de": "German",
  "el": "Greek",
  "gu": "Gujarati",
  "ht": "Haitian Creole",
  "iw": "Hebrew",
  "hi": "Hindi",
  "hu": "Hungarian",
  "is": "Icelandic",
  "id": "Indonesian",
  "ga": "Irish",
  "it": "Italian",
  "ja": "Japanese",
  "kn": "Kannada",
  "ko": "Korean",
  "la": "Latin",
  "lv": "Latvian",
  "lt": "Lithuanian",
  "mk": "Macedonian",
  "ms": "Malay",
  "mt": "Maltese",
  "no": "Norwegian",
  "fa": "Persian",
  "pl": "Polish",
  "pt": "Portuguese",
  "ro": "Romanian",
  "ru": "Russian",
  "sr": "Serbian",
  "sk": "Slovak",
  "sl": "Slovenian",
  "es": "Spanish",
  "sw": "Swahili",
  "sv": "Swedish",
  "ta": "Tamil",
  "te": "Telugu",
  "th": "Thai",
  "tr": "Turkish",
  "uk": "Ukrainian",
  "ur": "Urdu",
  "vi": "Vietnamese",
  "cy": "Welsh",
  "yi": "Yiddish"

getCode = (language) ->
  for code, lang in pairs languages
    if lang\lower! == language\lower!
      return code
    else if language\lower! == code\lower!
      return code

urlEncode = (str, space) ->
  space = space or '+'
  str = tostring(str)

  str = str\gsub '([^%w ])', (c) ->
    string.format  "%%%02X", string.byte(c)
  return str\gsub(' ', space)

buildQueryStr = (tbl) ->
  out = {}
  for k,v in pairs tbl
    table.insert out, k..'='..urlEncode(v)
  table.concat out, '&'

translate = (origin, target, term, cb) ->
  origin = getCode(origin) or 'auto'
  target = getCode(target) or 'en'
  args =
    client: 'p'
    hl: 'en'
    multires: 1
    sc: 1
    sl: origin
    ssel: 0
    tl: target
    tsel: 0
    uptl: "en"
    text: term

  simplehttp {
    url:"http://translate.google.com/translate_a/t?" .. buildQueryStr(args)
    headers: {
      "User-Agent": "Mozilla/5.0"
      "Accept-Charset": "UTF-8"
    }
  }, (data) ->
    if #data > 4
      parsed = json.decode data
      language = parsed.src
      out = table.concat [k.trans for k in *parsed.sentences]
      if #out > 0
        cb(out)

PRIVMSG:
  '^%ptranslate ([%w%-]+) ([%w%-]+) (.*)$': (source, destination, origin, target, term) =>
    translate origin, target, term, (text) ->
      say text
  '^%ptr ([%w%-]+) ([%w%-]+) (.*)$': (source, destination, origin, target, term) =>
    translate origin, target, term, (text) ->
      say text
  '^%ptlolno (.*)$': (source, destination, text) =>
    translate 'auto', 'zh-TW', text, (text) ->
      translate 'zh-TW', 'no', text, (text) ->
        say text
  '^%ptlol (.*)$': (source, destination, text) =>
    translate 'auto', 'zh-CN', text, (text) ->
      translate 'zh-CN', 'no', text, (text) ->
        translate 'no', 'ja', text, (text) ->
          translate 'ja', 'en', text, (text) ->
            say text