Override log level if global debug is true

-- Copyright (c) 2014 by Adam Hellberg <sharparam@sharparam.com>.
--
-- 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.

NAME, T = ...

import decorate from T.misc

{localization: L} = T

import format, upper from string

local db

logging_enabled = true
logging_level = 1
logging_color_enabled = true
db_loaded = false
debug_enabled = false

class T.Logger
    @levels:
        DEBUG: 0
        INFO: 1
        WARN: 2
        ERROR: 3
        FATAL: 4
        NOTICE: 5

    @level_to_prefix: (level) =>
        @prefixes[level] or L.unknown\upper!

    @prefix_to_level: (prefix) =>
        prefix = prefix\upper!
        @levels[prefix] or -1

    new: (@name, @color) =>

    log: (level, ...) =>
        return unless logging_enabled -- db 'log', true
        return if level == @@levels.DEBUG and (not db_loaded or not debug_enabled)
        -- Override the logging level if global debug option is enabled
        level = @@levels.DEBUG if debug_enabled
        return if level < logging_level
        prefix = @@level_to_prefix level
        docolor = logging_color_enabled
        name = docolor and decorate(NAME, @@colors.name) or NAME
        logname = (docolor and @color) and decorate(@name, @color) or @name
        prefix_color = docolor and @@colors.levels[level] or nil
        prefix = (docolor and prefix_color) and decorate(prefix, prefix_color) or prefix
        msg = '%s.%s: [%s] %s'\format name, logname, prefix, format ...
        DEFAULT_CHAT_FRAME\AddMessage msg

import Logger from T

Logger.prefixes = {v, k for k, v in pairs Logger.levels}

with Logger
    .colors =
        name: '00FF00' -- -> db 'log.color.name', '00FF00'
        levels:
            [.levels.DEBUG]: '008000' -- -> db 'log.color.level.debug',  '008000' -- Green
            [.levels.INFO]: 'FFFFFF'  -- -> db 'log.color.level.info',   'FFFFFF' -- White
            [.levels.WARN]: 'FFD700' --  -> db 'log.color.level.warn',   'FFD700' -- Gold
            [.levels.ERROR]: 'FF0000' -- -> db 'log.color.level.error',  'FF0000' -- Red
            [.levels.FATAL]: 'FF0000' -- -> db 'log.color.level.fatal',  'FF0000' -- Red
            [.levels.NOTICE]: '00FFFF' -- -> db 'log.color.level.notice', '00FFFF' -- Cyan

for id, level in pairs Logger.levels
    Logger.__base[id\lower!] = (...) => @log level, ...

log = Logger('main')

T.log = log

load_db = (event, database) ->

T.SHAREXP_DB_CREATED = (event, database) ->
    db = database if database.global_name == 'ShareXPDB'

T.SHAREXP_DB_LOADED = (event, database) ->
    return unless database.global_name == db.global_name
    logging_enabled = db 'log', logging_enabled
    logging_level = db 'log.level', logging_level
    logging_color_enabled = db 'log.color', logging_color_enabled
    db_loaded = true
    debug_enabled = db 'debug', debug_enabled
    for level, color in pairs Logger.colors.levels
        prefix = Logger\level_to_prefix(level)\lower!
        Logger.colors.levels[level] = db "log.color.level.#{prefix}", color

T.SHAREXP_DB_UPDATED = (event, db, key) ->
    return unless db.global_name == 'ShareXPDB'
    return unless key\match '^log'
    switch key
        when 'log'
            value = db 'log'
            logging_enabled = value
            log\notice L.log_status_changed, value and L.enabled or L.disabled
        when 'log.color'
            value = db 'log.color'
            logging_color_enabled = value
            log\notice L.log_coloring_changed, value and L.enabled or L.disabled
        when 'log.level'
            value = db 'log.level'
            logging_level = value
            log\notice L.log_level_changed, Logger\level_to_prefix value
        when 'log.color.name'
            color = db 'log.color.name'
            Logger.colors.name = color
            msg = L 'log_color_changed', L.name\gsub('^.', upper), color
            log\notice decorate(msg, color)
        else
            prefix = key\match '^log%.color%.level%.(%w+)$'
            if prefix
                prefix = prefix\upper!
                level = Logger\prefix_to_level prefix
                color = db key
                Logger.colors.levels[level] = color
                msg = L 'log_color_changed', prefix, color
                log\notice decorate(msg, color)

-- Copyright (c) 2014 by Adam Hellberg <sharparam@sharparam.com>.
--
-- 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.

NAME, T = ...

import decorate from T.misc

{localization: L} = T

import format, upper from string

local db

logging_enabled = true
logging_level = 1
logging_color_enabled = true
db_loaded = false
debug_enabled = false

class T.Logger
    @levels:
        DEBUG: 0
        INFO: 1
        WARN: 2
        ERROR: 3
        FATAL: 4
        NOTICE: 5

    @level_to_prefix: (level) =>
        @prefixes[level] or L.unknown\upper!

    @prefix_to_level: (prefix) =>
        prefix = prefix\upper!
        @levels[prefix] or -1

    new: (@name, @color) =>

    log: (level, ...) =>
        return unless logging_enabled -- db 'log', true
        return if level == @@levels.DEBUG and (not db_loaded or not debug_enabled)
        return if level < logging_level
        prefix = @@level_to_prefix level
        docolor = logging_color_enabled
        name = docolor and decorate(NAME, @@colors.name) or NAME
        logname = (docolor and @color) and decorate(@name, @color) or @name
        prefix_color = docolor and @@colors.levels[level] or nil
        prefix = (docolor and prefix_color) and decorate(prefix, prefix_color) or prefix
        msg = '%s.%s: [%s] %s'\format name, logname, prefix, format ...
        DEFAULT_CHAT_FRAME\AddMessage msg

import Logger from T

Logger.prefixes = {v, k for k, v in pairs Logger.levels}

with Logger
    .colors =
        name: '00FF00' -- -> db 'log.color.name', '00FF00'
        levels:
            [.levels.DEBUG]: '008000' -- -> db 'log.color.level.debug',  '008000' -- Green
            [.levels.INFO]: 'FFFFFF'  -- -> db 'log.color.level.info',   'FFFFFF' -- White
            [.levels.WARN]: 'FFD700' --  -> db 'log.color.level.warn',   'FFD700' -- Gold
            [.levels.ERROR]: 'FF0000' -- -> db 'log.color.level.error',  'FF0000' -- Red
            [.levels.FATAL]: 'FF0000' -- -> db 'log.color.level.fatal',  'FF0000' -- Red
            [.levels.NOTICE]: '00FFFF' -- -> db 'log.color.level.notice', '00FFFF' -- Cyan

for id, level in pairs Logger.levels
    Logger.__base[id\lower!] = (...) => @log level, ...

log = Logger('main')

T.log = log

load_db = (event, database) ->

T.SHAREXP_DB_CREATED = (event, database) ->
    db = database if database.global_name == 'ShareXPDB'

T.SHAREXP_DB_LOADED = (event, database) ->
    return unless database.global_name == db.global_name
    logging_enabled = db 'log', logging_enabled
    logging_level = db 'log.level', logging_level
    logging_color_enabled = db 'log.color', logging_color_enabled
    db_loaded = true
    debug_enabled = db 'debug', debug_enabled
    for level, color in pairs Logger.colors.levels
        prefix = Logger\level_to_prefix(level)\lower!
        Logger.colors.levels[level] = db "log.color.level.#{prefix}", color

T.SHAREXP_DB_UPDATED = (event, db, key) ->
    return unless db.global_name == 'ShareXPDB'
    return unless key\match '^log'
    switch key
        when 'log'
            value = db 'log'
            logging_enabled = value
            log\notice L.log_status_changed, value and L.enabled or L.disabled
        when 'log.color'
            value = db 'log.color'
            logging_color_enabled = value
            log\notice L.log_coloring_changed, value and L.enabled or L.disabled
        when 'log.level'
            value = db 'log.level'
            logging_level = value
            log\notice L.log_level_changed, Logger\level_to_prefix value
        when 'log.color.name'
            color = db 'log.color.name'
            Logger.colors.name = color
            msg = L 'log_color_changed', L.name\gsub('^.', upper), color
            log\notice decorate(msg, color)
        else
            prefix = key\match '^log%.color%.level%.(%w+)$'
            if prefix
                prefix = prefix\upper!
                level = Logger\prefix_to_level prefix
                color = db key
                Logger.colors.levels[level] = color
                msg = L 'log_color_changed', prefix, color
                log\notice decorate(msg, color)


github: only return part before \n

github: only return part before \n

Former-commit-id: c43c5e8be3080b44269cf4bb7afb3b4376fe773f [formerly f7f9ab0d100d516290fb680c2c3bf1c21bee7172]
Former-commit-id: aa67fd7da63c81d95d65b74d6ff2d89944e93008

util = require 'util'

ivar2.webserver.regUrl '/github/(.*)', (req, res) ->
  destination = req.url\match('/github/(.+)/?$')
  destination = destination\gsub '%%23', '#'
  json = util.json.decode(req.body)

  repo = json.repository.full_name
  branch = json.ref\gsub 'refs/heads/', ''

  for i, c in pairs(json.commits)
    if i > 3
      break
    message = c.message\gsub '\n.*', ''
    ivar2\Msg 'privmsg', destination, nil, "#{repo}: #{branch}, #{c.author.username}: #{message} #{c.url}"
  if #json.commits > 3
    ivar2\Msg 'privmsg', destination, nil, "#{repo}: #{branch}, #{#json.commits-3} more commits not displayed."

  res\set_status(200)
  res\set_header('Content-Type', 'text/plain')
  res\set_body('ok')
  res\send()

return {}

util = require 'util'

ivar2.webserver.regUrl '/github/(.*)', (req, res) ->
  destination = req.url\match('/github/(.+)/?$')
  destination = destination\gsub '%%23', '#'
  json = util.json.decode(req.body)

  repo = json.repository.full_name
  branch = json.ref\gsub 'refs/heads/', ''

  for i, c in pairs(json.commits)
    if i > 3
      break
    ivar2\Msg 'privmsg', destination, nil, "#{repo}: #{branch}, #{c.author.username}: #{c.message} #{c.url}"
  if #json.commits > 3
    ivar2\Msg 'privmsg', destination, nil, "#{repo}: #{branch}, #{#json.commits-3} more commits not displayed."

  res\set_status(200)
  res\set_header('Content-Type', 'text/plain')
  res\set_body('ok')
  res\send()

return {}


More sad

More sad

--
-- 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.
--
--
-- 0	eyes_updown
-- 1	eyes_rightleft
-- 2	JawOpen
-- 3	JawClose
-- 4	Smirk
-- 5	Frown
-- 6	Stretch
-- 7	Pucker
-- 8	Grin
-- 9	CatFace
-- 10	Mouth_O
-- 11	Mouth_O2
-- 12	Mouth_Full
-- 13	Tongue_Out
-- 14	Tongue_Up
-- 15	Tongue_Down
-- 16	NoEyelashes
-- 17	Eyes_Blink
-- 18	Left_Blink
-- 19	Right_Blink
-- 20	Scrunch
-- 21	FatButt
-- 22	Stomach_Out
-- 23	Stomach_In
-- 24	Throat_Bulge
--

DISABLE_FLEXES = CreateConVar('ppm2_disable_flexes', '0', {FCVAR_ARCHIVE}, 'Disable pony flexes controllers. Saves some FPS.')

class FlexState
    new: (controller, flexName = '', flexID = 0, scale = 1, speed = 1, min = 0, max = 1, useModifiers = true) =>
        @controller = controller
        @ent = controller.ent
        @name = flexName
        @flexName = flexName
        @flexID = flexID
        @id = flexID
        @scale = scale
        @speed = speed
        @originalscale = scale
        @originalspeed = speed
        @min = min
        @max = max
        @current = -1
        @target = 0
        @speedModify = 1
        @scaleModify = 1
        @speedModifiers = {}
        @scaleModifiers = {}
        @modifiers = {}
        @modifiersNames = {}
        @useModifiers = useModifiers
        @nextModifierID = 0

    GetFlexID: => @flexID
    GetFlexName: => @flexName

    GetModifierID: (name = '') =>
        return @modifiersNames[name] if @modifiersNames[name]
        @nextModifierID += 1
        id = @nextModifierID
        @modifiersNames[name] = id
        @speedModifiers[id] = 0
        @scaleModifiers[id] = 0
        @modifiers[id] = 0
        return id
    SetModifierWeight: (modifID, val = 0) =>
        return if not modifID
        return if not @modifiers[modifID]
        @modifiers[modifID] = val
    SetModifierScale: (modifID, val = 0) =>
        return if not modifID
        return if not @scaleModifiers[modifID]
        @scaleModifiers[modifID] = val
    SetModifierScale: (modifID, val = 0) =>
        return if not modifID
        return if not @speedModifiers[modifID]
        @speedModifiers[modifID] = val
    ResetModifiers: (name = '') =>
        return false if not @modifiersNames[name]
        id = @modifiersNames[name]
        @speedModifiers[id] = 0
        @scaleModifiers[id] = 0
        @modifiers[id] = 0
        return true
    GetEntity: => @ent
    GetData: => @controller
    GetController: => @controller
    GetValue: => @current
    GetRealValue: => @target
    SetValue: (val = @target) =>
        @current = math.Clamp(val, @min, @max) * @scale * @scaleModify
        @target = @target
    SetRealValue: (val = @target) => @target = math.Clamp(val, @min, @max) * @scale * @scaleModify

    GetScale: => @scale
    GetSpeed: => @speed
    GetScaleModify: => @scaleModify
    GetSpeedModify: => @speedModify
    GetOriginalScale: => @originalscale
    GetOriginalSpeed: => @originalspeed

    SetScale: (val = @scale) => @scale = val
    GetSpeed: (val = @speed) => @speed = val
    SetScaleModify: (val = @scaleModify) => @scaleModify = val
    GetSpeedModify: (val = @speedModify) => @speedModify = val

    AddValue: (val = 0) => @SetValue(@current + val)
    AddRealValue: (val = 0) => @SetRealValue(@target + val)
    Think: (delta = 0) =>
        if @useModifiers
            @target = 0
            @scale = @originalscale * @scaleModify
            @speed = @originalspeed * @speedModify
            @target += modif for modif in *@modifiers
            @scale += modif for modif in *@scaleModifiers
            @speed += modif for modif in *@speedModifiers
            @target = math.Clamp(@target, @min, @max) * @scale
        @ent = @controller.ent
        @current = Lerp(delta * 10 * @speed * @speedModify, @current, @target) if @current ~= @target
        @ent\SetFlexWeight(@flexID, @current)

class FlexSequence
    new: (controller, data) =>
        {
            'name': @name
            'repeat': @dorepeat
            'frames': @frames
            'time': @time
            'func': @func
            'create': @createfunc
            'ids': @flexIDsIterable
            'numid': @numid
        } = data

        @flexIDS = {}
        @flexStates = {}
        i = 1
        for id in *data.ids
            state = controller\GetFlexState(id)
            num = state\GetModifierID(@name)
            @["flex_#{id}"] = num
            @flexIDS[id] = num
            @flexStates[id] = state
            @flexStates[i] = state
            @flexIDS[i] = num
            i += 1

        @ent = controller.ent
        @controller = controller
        @frame = 0
        @start = RealTime()
        @finish = @start + @time
        @deltaAnim = 1
        @speed = 1
        @scale = 1
        @valid = true
        @createfunc() if @createfunc

    GetController: => @controller
    GetEntity: => @ent
    GetName: => @name
    GetRepeat: => @dorepeat
    GetFrames: => @frames
    GetFrame: => @frames
    GetTime: => @time
    GetThinkFunc: => @func
    GetCreatFunc: => @createfunc
    GetSpeed: => @speed
    GetAnimationSpeed: => @speed
    GetScale: => @scale
    GetModifierID: (id = '') => @flexIDS[id]
    GetFlexState: (id = '') => @flexStates[id]

    SetModifierWeight: (id = '', val = 0) => @GetFlexState(id)\SetModifierWeight(@GetModifierID(id), val)

    IsValid: => @valid
    Think: (delta = 0) =>
        @ent = @controller.ent
        return false if not IsValid(@ent)
        return false if not @func
        if @HasFinished()
            @Stop()
            return false

        @deltaAnim = (@finish - RealTime()) / @time
        if @deltaAnim < 0
            @deltaAnim = 1
            @frame = 0
            @start = RealTime()
            @finish = @start + @time
        @frame += 1

        status = @func(delta, 1 - @deltaAnim)
        if status == false
            @Stop()
            return false

        return true
    Stop: =>
        for id in *@flexIDsIterable
            @GetController()\GetFlexState(id)\ResetModifiers(@name)
        @valid = false
    Remove: => @Stop()
    HasFinished: =>
        return false if @dorepeat
        return RealTime() > @finish

class PonyFlexController
    @AVALIABLE_CONTROLLERS = {}
    @MODELS = {'models/ppm/player_default_base_new.mdl'}

    @FLEX_LIST = {
        {flex: 'eyes_updown',       scale: 1, speed: 1}
        {flex: 'eyes_rightleft',    scale: 1, speed: 1}
        {flex: 'JawOpen',           scale: 1, speed: 1}
        {flex: 'JawClose',          scale: 1, speed: 1}
        {flex: 'Smirk',             scale: 1, speed: 1}
        {flex: 'Frown',             scale: 1, speed: 1}
        {flex: 'Stretch',           scale: 1, speed: 1}
        {flex: 'Pucker',            scale: 1, speed: 1}
        {flex: 'Grin',              scale: 1, speed: 1}
        {flex: 'CatFace',           scale: 1, speed: 1}
        {flex: 'Mouth_O',           scale: 1, speed: 1}
        {flex: 'Mouth_O2',          scale: 1, speed: 1}
        {flex: 'Mouth_Full',        scale: 1, speed: 1}
        {flex: 'Tongue_Out',        scale: 1, speed: 1}
        {flex: 'Tongue_Up',         scale: 1, speed: 1}
        {flex: 'Tongue_Down',       scale: 1, speed: 1}
        {flex: 'NoEyelashes',       scale: 1, speed: 1}
        {flex: 'Eyes_Blink',        scale: 1, speed: 1}
        {flex: 'Left_Blink',        scale: 1, speed: 1}
        {flex: 'Right_Blink',       scale: 1, speed: 1}
        {flex: 'Scrunch',           scale: 1, speed: 1}
        {flex: 'FatButt',           scale: 1, speed: 1}
        {flex: 'Stomach_Out',       scale: 1, speed: 1}
        {flex: 'Stomach_In',        scale: 1, speed: 1}
        {flex: 'Throat_Bulge',      scale: 1, speed: 1}
    }

    @FLEX_SEQUENCES = {
        {
            'name': 'eyes_idle'
            'autostart': true
            'repeat': true
            'time': 5
            'ids': {'Left_Blink', 'Right_Blink'}
            'func': (delta, timeOfAnim) =>
                left, right = @GetModifierID(1), @GetModifierID(2)
                leftState, rightState = @GetFlexState(1), @GetFlexState(2)
                value = math.abs(math.sin(RealTime() * .5) * .15)
                leftState\SetModifierWeight(left, value)
                rightState\SetModifierWeight(right, value)
        }

        {
            'name': 'body_idle'
            'autostart': true
            'repeat': true
            'time': 2
            'ids': {'Stomach_Out', 'Stomach_In'}
            'func': (delta, timeOfAnim) =>
                In, Out = @GetModifierID(1), @GetModifierID(2)
                InState, OutState = @GetFlexState(1), @GetFlexState(2)
                abs = math.abs(0.5 - timeOfAnim)
                InState\SetModifierWeight(In, abs)
                OutState\SetModifierWeight(Out, abs)
        }

        {
            'name': 'health_idle'
            'autostart': true
            'repeat': true
            'time': 5
            'ids': {'Frown', 'Left_Blink', 'Right_Blink', 'Scrunch', 'Mouth_O', 'JawOpen'}
            'func': (delta, timeOfAnim) =>
                frown = @GetModifierID(1)
                frownState = @GetFlexState(1)
                left, right = @GetModifierID(2), @GetModifierID(3)
                leftState, rightState = @GetFlexState(2), @GetFlexState(3)
                Mouth_O, Mouth_OState = @GetModifierID(4), @GetFlexState(4)
                Scrunch = @GetModifierID(4)
                ScrunchState = @GetFlexState(4)

                hp, mhp = @ent\Health(), @ent\GetMaxHealth()
                mhp = 1 if mhp == 0
                strength = math.Clamp(1.5 - (hp / mhp) * 1.5, 0, 1)
                frownState\SetModifierWeight(frown, strength)
                ScrunchState\SetModifierWeight(Scrunch, strength * .5)
                leftState\SetModifierWeight(left, strength * .1)
                rightState\SetModifierWeight(right, strength * .1)
                Mouth_OState\SetModifierWeight(Mouth_O, strength * .8)

                JawOpen = @GetModifierID(6)
                JawOpenState = @GetFlexState(6)

                if strength > .75
                    JawOpenState\SetModifierWeight(JawOpen, strength * .2 + math.sin(RealTime() * strength * 3) * .1)
                else
                    JawOpenState\SetModifierWeight(JawOpen, 0)
        }

        {
            'name': 'greeny'
            'autostart': false
            'repeat': false
            'time': 2
            'ids': {'Grin'}
            'func': (delta, timeOfAnim) =>
                Grin = @GetModifierID(1)
                GrinState = @GetFlexState(1)
                strength = .5 + math.sin(RealTime() * 2) * .25
                GrinState\SetModifierWeight(Grin, strength)
        }

        {
            'name': 'big_grin'
            'autostart': false
            'repeat': false
            'time': 3
            'ids': {'Grin'}
            'func': (delta, timeOfAnim) =>
                Grin = @GetModifierID(1)
                GrinState = @GetFlexState(1)
                GrinState\SetModifierWeight(Grin, 1)
        }

        {
            'name': 'xd'
            'autostart': false
            'repeat': false
            'time': 3
            'ids': {'Grin', 'Left_Blink', 'Right_Blink', 'JawOpen'}
            'func': (delta, timeOfAnim) =>
                Grin = @GetModifierID(1)
                GrinState = @GetFlexState(1)
                GrinState\SetModifierWeight(Grin, .6)

                Left_Blink = @GetModifierID(2)
                Left_BlinkState = @GetFlexState(2)
                Left_BlinkState\SetModifierWeight(Left_Blink, .9)

                Right_Blink = @GetModifierID(3)
                Right_BlinkState = @GetFlexState(3)
                Right_BlinkState\SetModifierWeight(Right_Blink, .9)

                JawOpen = @GetModifierID(4)
                JawOpenState = @GetFlexState(4)
                JawOpenState\SetModifierScale(JawOpen, 2)
                JawOpenState\SetModifierWeight(JawOpen, (timeOfAnim % .1) * 2)
        }

        {
            'name': 'tongue'
            'autostart': false
            'repeat': false
            'time': 3
            'ids': {'JawOpen', 'Tongue_Out'}
            'func': (delta, timeOfAnim) =>
                @SetModifierWeight(1, .1)
                @SetModifierWeight(2, 1)
        }

        {
            'name': 'sad'
            'autostart': false
            'repeat': false
            'time': 4
            'ids': {'Frown'}
            'func': (delta, timeOfAnim) =>
                @SetModifierWeight(1, 1)
        }

        {
            'name': 'cat'
            'autostart': false
            'repeat': false
            'time': 2
            'ids': {'CatFace'}
            'func': (delta, timeOfAnim) =>
                Grin = @GetModifierID(1)
                GrinState = @GetFlexState(1)
                GrinState\SetModifierWeight(Grin, 1)
        }

        {
            'name': 'ooo'
            'autostart': false
            'repeat': false
            'time': 2
            'ids': {'Mouth_O2', 'Mouth_O'}
            'func': (delta, timeOfAnim) =>
                timeOfAnim *= 2
                Grin = @GetModifierID(1)
                GrinState = @GetFlexState(1)
                GrinState\SetModifierWeight(Grin, timeOfAnim)
                Grin = @GetModifierID(2)
                GrinState = @GetFlexState(2)
                GrinState\SetModifierWeight(Grin, timeOfAnim)
        }

        {
            'name': 'talk'
            'autostart': false
            'repeat': false
            'time': 2
            'ids': {'JawOpen', 'Tongue_Out', 'Tongue_Up', 'Tongue_Down'}
            'create': =>
                @talkAnim = for i = 0, 1, 0.05
                    rand = math.random(1, 100) / 100
                    if rand <= .25
                        {1 * rand, 0.4 * rand, 2 * rand, 0}
                    elseif rand >= .25 and rand < .4
                        rand *= .8
                        {2 * rand, .6 * rand, 0, 1 * rand}
                    elseif rand >= .4 and rand < .75
                        rand *= .6
                        {1 * rand, 0, 1 * rand, 2 * rand}
                    elseif rand >= .75
                        rand *= .4
                        {1.5 * rand, 0, 1 * rand, 0}
            'func': (delta, timeOfAnim) =>
                JawOpen = @GetModifierID(1)
                JawOpenState = @GetFlexState(1)
                Tongue_OutOpen = @GetModifierID(2)
                Tongue_OutOpenState = @GetFlexState(2)
                Tongue_UpOpen = @GetModifierID(3)
                Tongue_UpOpenState = @GetFlexState(3)
                Tongue_DownOpen = @GetModifierID(4)
                Tongue_DownOpenState = @GetFlexState(4)
                cPos = math.floor(timeOfAnim * 20) + 1
                data = @talkAnim[cPos]
                return if not data
                {jaw, out, up, down} = data
                JawOpenState\SetModifierWeight(JawOpen, jaw)
                Tongue_OutOpenState\SetModifierWeight(Tongue_OutOpen, out)
                Tongue_UpOpenState\SetModifierWeight(Tongue_UpOpen, up)
                Tongue_DownOpenState\SetModifierWeight(Tongue_DownOpen, down)
        }

        {
            'name': 'talk_endless'
            'autostart': false
            'repeat': true
            'time': 4
            'ids': {'JawOpen', 'Tongue_Out', 'Tongue_Up', 'Tongue_Down'}
            'create': =>
                @talkAnim = for i = 0, 1, 0.05
                    rand = math.random(1, 100) / 100
                    if rand <= .25
                        {1 * rand, 0.4 * rand, 2 * rand, 0}
                    elseif rand >= .25 and rand < .4
                        rand *= .8
                        {2 * rand, .6 * rand, 0, 1 * rand}
                    elseif rand >= .4 and rand < .75
                        rand *= .6
                        {1 * rand, 0, 1 * rand, 2 * rand}
                    elseif rand >= .75
                        rand *= .4
                        {1.5 * rand, 0, 1 * rand, 0}
            'func': (delta, timeOfAnim) =>
                JawOpen = @GetModifierID(1)
                JawOpenState = @GetFlexState(1)
                Tongue_OutOpen = @GetModifierID(2)
                Tongue_OutOpenState = @GetFlexState(2)
                Tongue_UpOpen = @GetModifierID(3)
                Tongue_UpOpenState = @GetFlexState(3)
                Tongue_DownOpen = @GetModifierID(4)
                Tongue_DownOpenState = @GetFlexState(4)
                cPos = math.floor(timeOfAnim * 20) + 1
                data = @talkAnim[cPos]
                return if not data
                {jaw, out, up, down} = data
                volume = @ent\VoiceVolume() * 6
                jaw *= volume
                out *= volume
                up *= volume
                down *= volume
                JawOpenState\SetModifierWeight(JawOpen, jaw)
                Tongue_OutOpenState\SetModifierWeight(Tongue_OutOpen, out)
                Tongue_UpOpenState\SetModifierWeight(Tongue_UpOpen, up)
                Tongue_DownOpenState\SetModifierWeight(Tongue_DownOpen, down)
        }

        {
            'name': 'eyes_blink'
            'autostart': true
            'repeat': true
            'time': 7
            'ids': {'Left_Blink', 'Right_Blink'}
            'create': =>
                @nextBlink = math.random(300, 600) / 1000
                @nextBlinkLength = math.random(50, 75) / 1000
                @min, @max = @nextBlink, @nextBlink + @nextBlinkLength
            'func': (delta, timeOfAnim) =>
                if @min > timeOfAnim or @max < timeOfAnim
                    if @blinkHit
                        @blinkHit = false
                        left, right = @GetModifierID(1), @GetModifierID(2)
                        leftState, rightState = @GetFlexState(1), @GetFlexState(2)
                        leftState\SetModifierWeight(left, 0)
                        rightState\SetModifierWeight(right, 0)
                    return
                left, right = @GetModifierID(1), @GetModifierID(2)
                leftState, rightState = @GetFlexState(1), @GetFlexState(2)
                leftState\SetModifierWeight(left, .9)
                rightState\SetModifierWeight(right, .9)
                @blinkHit = true
        }
    }

    @__inherited: (child) =>
        child.MODELS_HASH = {mod, true for mod in *child.MODELS}
        @AVALIABLE_CONTROLLERS[mod] = child for mod in *child.MODELS
        for i, flex in pairs child.FLEX_LIST
            flex.id = i - 1
            flex.targetName = "target#{flex.flex}"
        child.FLEX_IDS = {flex.id, flex for flex in *child.FLEX_LIST}
        child.FLEX_TABLE = {flex.flex, flex for flex in *child.FLEX_LIST}
        seq.numid = i for i, seq in pairs child.FLEX_SEQUENCES
        child.FLEX_SEQUENCES_TABLE = {seq.name, seq for seq in *child.FLEX_SEQUENCES}
        child.FLEX_SEQUENCES_TABLE[seq.numid] = seq for seq in *child.FLEX_SEQUENCES
    @__inherited(@)

    @NEXT_HOOK_ID = 0

    new: (data) =>
        @controller = data
        @ent = data.ent
        @states = [FlexState(@, flex, id, scale, speed) for {:flex, :id, :scale, :speed} in *@@FLEX_LIST]
        @statesTable = {state\GetFlexName(), state for state in *@states}
        @statesTable[state\GetFlexName()\lower()] = state for state in *@states
        @statesTable[state\GetFlexID()] = state for state in *@states
        @hooks = {}
        @@NEXT_HOOK_ID += 1
        @fid = @@NEXT_HOOK_ID
        @hookID = "PPM2.FlexController.#{@@NEXT_HOOK_ID}"
        @lastThink = RealTime()
        @currentSequences = {}
        @currentSequencesIterable = {}
        @ResetSequences()
        @Hook('OnPlayerChat', @OnPlayerChat)
        @Hook('PlayerStartVoice', @PlayerStartVoice)
        @Hook('PlayerEndVoice', @PlayerEndVoice)

    StartSequence: (seqID = '') =>
        return @currentSequences[seqID] if @currentSequences[seqID]
        return if not @@FLEX_SEQUENCES_TABLE[seqID]
        @currentSequences[seqID] = FlexSequence(@, @@FLEX_SEQUENCES_TABLE[seqID])
        @currentSequencesIterable = [seq for i, seq in pairs @currentSequences]
        return @currentSequences[seqID]

    EndSequence: (seqID = '', callStop = true) =>
        return false if not @currentSequences[seqID]
        @currentSequences[seqID]\Stop() if callStop
        @currentSequences[seqID] = nil
        @currentSequencesIterable = [seq for i, seq in pairs @currentSequences]
        return true

    ResetSequences: =>
        for seq in *@currentSequencesIterable
            seq\Stop()

        @currentSequences = {}
        @currentSequencesIterable = {}
        state\Think(1000) for state in *@states

        for seq in *@@FLEX_SEQUENCES
            continue if not seq.autostart
            @StartSequence(seq.name)

    PlayerRespawn: =>
        @ResetSequences()

    HasSequence: (seqID = '') => @currentSequences[seqID] and true or false

    GetFlexState: (name = '') => @statesTable[name]
    DataChanges: (state) =>
    GetEntity: => @ent
    GetData: => @controller
    GetController: => @controller

    Hook: (id, func) =>
        newFunc = (...) ->
            if not IsValid(@ent)
                @ent = @GetData().ent
            if not IsValid(@ent) or @GetData()\GetData() ~= @ent\GetPonyData()
                @RemoveHooks()
                return
            func(@, ...)
            return nil
        hook.Add id, @hookID, newFunc
        table.insert(@hooks, id)

    OnPlayerChat: (ply = NULL, text = '', teamOnly = false, isDead = false) =>
        return if ply ~= @ent or teamOnly or isDead
        switch text\lower()
            when 'o'
                @StartSequence('ooo')
            when ':o'
                @StartSequence('ooo')
            when 'о'
                @StartSequence('ooo')
            when 'О'
                @StartSequence('ooo')
            when ':о'
                @StartSequence('ooo')
            when ':О'
                @StartSequence('ooo')
            when ':3'
                @StartSequence('cat')
            when ':з'
                @StartSequence('cat')
            when ':d'
                @StartSequence('big_grin')
            when 'xd'
                @StartSequence('xd')
            when 'exdi'
                @StartSequence('xd')
            when ':p'
                @StartSequence('tongue')
            when ':р'
                @StartSequence('tongue')
            when ':Р'
                @StartSequence('tongue')
            when ':c'
                @StartSequence('sad')
            when ':('
                @StartSequence('sad')
            when '('
                @StartSequence('sad')
            when ':с'
                @StartSequence('sad')
            when ':С'
                @StartSequence('sad')
            else
                if string.find(text, 'hehehe') or string.find(text, 'hahaha')
                    @StartSequence('greeny')
                else
                    @StartSequence('talk')
    PlayerStartVoice: (ply = NULL) =>
        return if ply ~= @ent
        @StartSequence('talk_endless')
    PlayerEndVoice: (ply = NULL) =>
        return if ply ~= @ent
        @EndSequence('talk_endless')

    RemoveHooks: =>
        for iHook in *@hooks
            hook.Remove iHook, @hookID

    Think: =>
        return if DISABLE_FLEXES\GetBool()
        if not IsValid(@ent)
            @ent = @GetData().ent
        return if not IsValid(@ent) or @ent\IsDormant() or not @ent\Alive()
        delta = RealTime() - @lastThink
        @lastThink = RealTime()
        state\Think(delta) for state in *@states
        for seq in *@currentSequencesIterable
            if not seq\IsValid()
                @EndSequence(seq\GetName(), false)
                break
            seq\Think(delta)

do
    ppm2_disable_flexes = (cvar, oldval, newval) ->
        for ply in *player.GetAll()
            data = ply\GetPonyData()
            continue if not data
            flex = data\GetFlexController()
            continue if not flex
            flex\ResetSequences()
    cvars.AddChangeCallback 'ppm2_disable_flexes', ppm2_disable_flexes, 'ppm2_disable_flexes'

PPM2.PonyFlexController = PonyFlexController
PPM2.GetFlexController = (model = 'models/ppm/player_default_base_new.mdl') -> PonyFlexController.AVALIABLE_CONTROLLERS[model]

--
-- 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.
--
--
-- 0	eyes_updown
-- 1	eyes_rightleft
-- 2	JawOpen
-- 3	JawClose
-- 4	Smirk
-- 5	Frown
-- 6	Stretch
-- 7	Pucker
-- 8	Grin
-- 9	CatFace
-- 10	Mouth_O
-- 11	Mouth_O2
-- 12	Mouth_Full
-- 13	Tongue_Out
-- 14	Tongue_Up
-- 15	Tongue_Down
-- 16	NoEyelashes
-- 17	Eyes_Blink
-- 18	Left_Blink
-- 19	Right_Blink
-- 20	Scrunch
-- 21	FatButt
-- 22	Stomach_Out
-- 23	Stomach_In
-- 24	Throat_Bulge
--

DISABLE_FLEXES = CreateConVar('ppm2_disable_flexes', '0', {FCVAR_ARCHIVE}, 'Disable pony flexes controllers. Saves some FPS.')

class FlexState
    new: (controller, flexName = '', flexID = 0, scale = 1, speed = 1, min = 0, max = 1, useModifiers = true) =>
        @controller = controller
        @ent = controller.ent
        @name = flexName
        @flexName = flexName
        @flexID = flexID
        @id = flexID
        @scale = scale
        @speed = speed
        @originalscale = scale
        @originalspeed = speed
        @min = min
        @max = max
        @current = -1
        @target = 0
        @speedModify = 1
        @scaleModify = 1
        @speedModifiers = {}
        @scaleModifiers = {}
        @modifiers = {}
        @modifiersNames = {}
        @useModifiers = useModifiers
        @nextModifierID = 0

    GetFlexID: => @flexID
    GetFlexName: => @flexName

    GetModifierID: (name = '') =>
        return @modifiersNames[name] if @modifiersNames[name]
        @nextModifierID += 1
        id = @nextModifierID
        @modifiersNames[name] = id
        @speedModifiers[id] = 0
        @scaleModifiers[id] = 0
        @modifiers[id] = 0
        return id
    SetModifierWeight: (modifID, val = 0) =>
        return if not modifID
        return if not @modifiers[modifID]
        @modifiers[modifID] = val
    SetModifierScale: (modifID, val = 0) =>
        return if not modifID
        return if not @scaleModifiers[modifID]
        @scaleModifiers[modifID] = val
    SetModifierScale: (modifID, val = 0) =>
        return if not modifID
        return if not @speedModifiers[modifID]
        @speedModifiers[modifID] = val
    ResetModifiers: (name = '') =>
        return false if not @modifiersNames[name]
        id = @modifiersNames[name]
        @speedModifiers[id] = 0
        @scaleModifiers[id] = 0
        @modifiers[id] = 0
        return true
    GetEntity: => @ent
    GetData: => @controller
    GetController: => @controller
    GetValue: => @current
    GetRealValue: => @target
    SetValue: (val = @target) =>
        @current = math.Clamp(val, @min, @max) * @scale * @scaleModify
        @target = @target
    SetRealValue: (val = @target) => @target = math.Clamp(val, @min, @max) * @scale * @scaleModify

    GetScale: => @scale
    GetSpeed: => @speed
    GetScaleModify: => @scaleModify
    GetSpeedModify: => @speedModify
    GetOriginalScale: => @originalscale
    GetOriginalSpeed: => @originalspeed

    SetScale: (val = @scale) => @scale = val
    GetSpeed: (val = @speed) => @speed = val
    SetScaleModify: (val = @scaleModify) => @scaleModify = val
    GetSpeedModify: (val = @speedModify) => @speedModify = val

    AddValue: (val = 0) => @SetValue(@current + val)
    AddRealValue: (val = 0) => @SetRealValue(@target + val)
    Think: (delta = 0) =>
        if @useModifiers
            @target = 0
            @scale = @originalscale * @scaleModify
            @speed = @originalspeed * @speedModify
            @target += modif for modif in *@modifiers
            @scale += modif for modif in *@scaleModifiers
            @speed += modif for modif in *@speedModifiers
            @target = math.Clamp(@target, @min, @max) * @scale
        @ent = @controller.ent
        @current = Lerp(delta * 10 * @speed * @speedModify, @current, @target) if @current ~= @target
        @ent\SetFlexWeight(@flexID, @current)

class FlexSequence
    new: (controller, data) =>
        {
            'name': @name
            'repeat': @dorepeat
            'frames': @frames
            'time': @time
            'func': @func
            'create': @createfunc
            'ids': @flexIDsIterable
            'numid': @numid
        } = data

        @flexIDS = {}
        @flexStates = {}
        i = 1
        for id in *data.ids
            state = controller\GetFlexState(id)
            num = state\GetModifierID(@name)
            @["flex_#{id}"] = num
            @flexIDS[id] = num
            @flexStates[id] = state
            @flexStates[i] = state
            @flexIDS[i] = num
            i += 1

        @ent = controller.ent
        @controller = controller
        @frame = 0
        @start = RealTime()
        @finish = @start + @time
        @deltaAnim = 1
        @speed = 1
        @scale = 1
        @valid = true
        @createfunc() if @createfunc

    GetController: => @controller
    GetEntity: => @ent
    GetName: => @name
    GetRepeat: => @dorepeat
    GetFrames: => @frames
    GetFrame: => @frames
    GetTime: => @time
    GetThinkFunc: => @func
    GetCreatFunc: => @createfunc
    GetSpeed: => @speed
    GetAnimationSpeed: => @speed
    GetScale: => @scale
    GetModifierID: (id = '') => @flexIDS[id]
    GetFlexState: (id = '') => @flexStates[id]

    SetModifierWeight: (id = '', val = 0) => @GetFlexState(id)\SetModifierWeight(@GetModifierID(id), val)

    IsValid: => @valid
    Think: (delta = 0) =>
        @ent = @controller.ent
        return false if not IsValid(@ent)
        return false if not @func
        if @HasFinished()
            @Stop()
            return false

        @deltaAnim = (@finish - RealTime()) / @time
        if @deltaAnim < 0
            @deltaAnim = 1
            @frame = 0
            @start = RealTime()
            @finish = @start + @time
        @frame += 1

        status = @func(delta, 1 - @deltaAnim)
        if status == false
            @Stop()
            return false

        return true
    Stop: =>
        for id in *@flexIDsIterable
            @GetController()\GetFlexState(id)\ResetModifiers(@name)
        @valid = false
    Remove: => @Stop()
    HasFinished: =>
        return false if @dorepeat
        return RealTime() > @finish

class PonyFlexController
    @AVALIABLE_CONTROLLERS = {}
    @MODELS = {'models/ppm/player_default_base_new.mdl'}

    @FLEX_LIST = {
        {flex: 'eyes_updown',       scale: 1, speed: 1}
        {flex: 'eyes_rightleft',    scale: 1, speed: 1}
        {flex: 'JawOpen',           scale: 1, speed: 1}
        {flex: 'JawClose',          scale: 1, speed: 1}
        {flex: 'Smirk',             scale: 1, speed: 1}
        {flex: 'Frown',             scale: 1, speed: 1}
        {flex: 'Stretch',           scale: 1, speed: 1}
        {flex: 'Pucker',            scale: 1, speed: 1}
        {flex: 'Grin',              scale: 1, speed: 1}
        {flex: 'CatFace',           scale: 1, speed: 1}
        {flex: 'Mouth_O',           scale: 1, speed: 1}
        {flex: 'Mouth_O2',          scale: 1, speed: 1}
        {flex: 'Mouth_Full',        scale: 1, speed: 1}
        {flex: 'Tongue_Out',        scale: 1, speed: 1}
        {flex: 'Tongue_Up',         scale: 1, speed: 1}
        {flex: 'Tongue_Down',       scale: 1, speed: 1}
        {flex: 'NoEyelashes',       scale: 1, speed: 1}
        {flex: 'Eyes_Blink',        scale: 1, speed: 1}
        {flex: 'Left_Blink',        scale: 1, speed: 1}
        {flex: 'Right_Blink',       scale: 1, speed: 1}
        {flex: 'Scrunch',           scale: 1, speed: 1}
        {flex: 'FatButt',           scale: 1, speed: 1}
        {flex: 'Stomach_Out',       scale: 1, speed: 1}
        {flex: 'Stomach_In',        scale: 1, speed: 1}
        {flex: 'Throat_Bulge',      scale: 1, speed: 1}
    }

    @FLEX_SEQUENCES = {
        {
            'name': 'eyes_idle'
            'autostart': true
            'repeat': true
            'time': 5
            'ids': {'Left_Blink', 'Right_Blink'}
            'func': (delta, timeOfAnim) =>
                left, right = @GetModifierID(1), @GetModifierID(2)
                leftState, rightState = @GetFlexState(1), @GetFlexState(2)
                value = math.abs(math.sin(RealTime() * .5) * .15)
                leftState\SetModifierWeight(left, value)
                rightState\SetModifierWeight(right, value)
        }

        {
            'name': 'body_idle'
            'autostart': true
            'repeat': true
            'time': 2
            'ids': {'Stomach_Out', 'Stomach_In'}
            'func': (delta, timeOfAnim) =>
                In, Out = @GetModifierID(1), @GetModifierID(2)
                InState, OutState = @GetFlexState(1), @GetFlexState(2)
                abs = math.abs(0.5 - timeOfAnim)
                InState\SetModifierWeight(In, abs)
                OutState\SetModifierWeight(Out, abs)
        }

        {
            'name': 'health_idle'
            'autostart': true
            'repeat': true
            'time': 5
            'ids': {'Frown', 'Left_Blink', 'Right_Blink', 'Scrunch', 'Mouth_O', 'JawOpen'}
            'func': (delta, timeOfAnim) =>
                frown = @GetModifierID(1)
                frownState = @GetFlexState(1)
                left, right = @GetModifierID(2), @GetModifierID(3)
                leftState, rightState = @GetFlexState(2), @GetFlexState(3)
                Mouth_O, Mouth_OState = @GetModifierID(4), @GetFlexState(4)
                Scrunch = @GetModifierID(4)
                ScrunchState = @GetFlexState(4)

                hp, mhp = @ent\Health(), @ent\GetMaxHealth()
                mhp = 1 if mhp == 0
                strength = math.Clamp(1.5 - (hp / mhp) * 1.5, 0, 1)
                frownState\SetModifierWeight(frown, strength)
                ScrunchState\SetModifierWeight(Scrunch, strength * .5)
                leftState\SetModifierWeight(left, strength * .1)
                rightState\SetModifierWeight(right, strength * .1)
                Mouth_OState\SetModifierWeight(Mouth_O, strength * .8)

                JawOpen = @GetModifierID(6)
                JawOpenState = @GetFlexState(6)

                if strength > .75
                    JawOpenState\SetModifierWeight(JawOpen, strength * .2 + math.sin(RealTime() * strength * 3) * .1)
                else
                    JawOpenState\SetModifierWeight(JawOpen, 0)
        }

        {
            'name': 'greeny'
            'autostart': false
            'repeat': false
            'time': 2
            'ids': {'Grin'}
            'func': (delta, timeOfAnim) =>
                Grin = @GetModifierID(1)
                GrinState = @GetFlexState(1)
                strength = .5 + math.sin(RealTime() * 2) * .25
                GrinState\SetModifierWeight(Grin, strength)
        }

        {
            'name': 'big_grin'
            'autostart': false
            'repeat': false
            'time': 3
            'ids': {'Grin'}
            'func': (delta, timeOfAnim) =>
                Grin = @GetModifierID(1)
                GrinState = @GetFlexState(1)
                GrinState\SetModifierWeight(Grin, 1)
        }

        {
            'name': 'xd'
            'autostart': false
            'repeat': false
            'time': 3
            'ids': {'Grin', 'Left_Blink', 'Right_Blink', 'JawOpen'}
            'func': (delta, timeOfAnim) =>
                Grin = @GetModifierID(1)
                GrinState = @GetFlexState(1)
                GrinState\SetModifierWeight(Grin, .6)

                Left_Blink = @GetModifierID(2)
                Left_BlinkState = @GetFlexState(2)
                Left_BlinkState\SetModifierWeight(Left_Blink, .9)

                Right_Blink = @GetModifierID(3)
                Right_BlinkState = @GetFlexState(3)
                Right_BlinkState\SetModifierWeight(Right_Blink, .9)

                JawOpen = @GetModifierID(4)
                JawOpenState = @GetFlexState(4)
                JawOpenState\SetModifierScale(JawOpen, 2)
                JawOpenState\SetModifierWeight(JawOpen, (timeOfAnim % .1) * 2)
        }

        {
            'name': 'tongue'
            'autostart': false
            'repeat': false
            'time': 3
            'ids': {'JawOpen', 'Tongue_Out'}
            'func': (delta, timeOfAnim) =>
                @SetModifierWeight(1, .1)
                @SetModifierWeight(2, 1)
        }

        {
            'name': 'sad'
            'autostart': false
            'repeat': false
            'time': 4
            'ids': {'Frown'}
            'func': (delta, timeOfAnim) =>
                @SetModifierWeight(1, 1)
        }

        {
            'name': 'cat'
            'autostart': false
            'repeat': false
            'time': 2
            'ids': {'CatFace'}
            'func': (delta, timeOfAnim) =>
                Grin = @GetModifierID(1)
                GrinState = @GetFlexState(1)
                GrinState\SetModifierWeight(Grin, 1)
        }

        {
            'name': 'ooo'
            'autostart': false
            'repeat': false
            'time': 2
            'ids': {'Mouth_O2', 'Mouth_O'}
            'func': (delta, timeOfAnim) =>
                timeOfAnim *= 2
                Grin = @GetModifierID(1)
                GrinState = @GetFlexState(1)
                GrinState\SetModifierWeight(Grin, timeOfAnim)
                Grin = @GetModifierID(2)
                GrinState = @GetFlexState(2)
                GrinState\SetModifierWeight(Grin, timeOfAnim)
        }

        {
            'name': 'talk'
            'autostart': false
            'repeat': false
            'time': 2
            'ids': {'JawOpen', 'Tongue_Out', 'Tongue_Up', 'Tongue_Down'}
            'create': =>
                @talkAnim = for i = 0, 1, 0.05
                    rand = math.random(1, 100) / 100
                    if rand <= .25
                        {1 * rand, 0.4 * rand, 2 * rand, 0}
                    elseif rand >= .25 and rand < .4
                        rand *= .8
                        {2 * rand, .6 * rand, 0, 1 * rand}
                    elseif rand >= .4 and rand < .75
                        rand *= .6
                        {1 * rand, 0, 1 * rand, 2 * rand}
                    elseif rand >= .75
                        rand *= .4
                        {1.5 * rand, 0, 1 * rand, 0}
            'func': (delta, timeOfAnim) =>
                JawOpen = @GetModifierID(1)
                JawOpenState = @GetFlexState(1)
                Tongue_OutOpen = @GetModifierID(2)
                Tongue_OutOpenState = @GetFlexState(2)
                Tongue_UpOpen = @GetModifierID(3)
                Tongue_UpOpenState = @GetFlexState(3)
                Tongue_DownOpen = @GetModifierID(4)
                Tongue_DownOpenState = @GetFlexState(4)
                cPos = math.floor(timeOfAnim * 20) + 1
                data = @talkAnim[cPos]
                return if not data
                {jaw, out, up, down} = data
                JawOpenState\SetModifierWeight(JawOpen, jaw)
                Tongue_OutOpenState\SetModifierWeight(Tongue_OutOpen, out)
                Tongue_UpOpenState\SetModifierWeight(Tongue_UpOpen, up)
                Tongue_DownOpenState\SetModifierWeight(Tongue_DownOpen, down)
        }

        {
            'name': 'talk_endless'
            'autostart': false
            'repeat': true
            'time': 4
            'ids': {'JawOpen', 'Tongue_Out', 'Tongue_Up', 'Tongue_Down'}
            'create': =>
                @talkAnim = for i = 0, 1, 0.05
                    rand = math.random(1, 100) / 100
                    if rand <= .25
                        {1 * rand, 0.4 * rand, 2 * rand, 0}
                    elseif rand >= .25 and rand < .4
                        rand *= .8
                        {2 * rand, .6 * rand, 0, 1 * rand}
                    elseif rand >= .4 and rand < .75
                        rand *= .6
                        {1 * rand, 0, 1 * rand, 2 * rand}
                    elseif rand >= .75
                        rand *= .4
                        {1.5 * rand, 0, 1 * rand, 0}
            'func': (delta, timeOfAnim) =>
                JawOpen = @GetModifierID(1)
                JawOpenState = @GetFlexState(1)
                Tongue_OutOpen = @GetModifierID(2)
                Tongue_OutOpenState = @GetFlexState(2)
                Tongue_UpOpen = @GetModifierID(3)
                Tongue_UpOpenState = @GetFlexState(3)
                Tongue_DownOpen = @GetModifierID(4)
                Tongue_DownOpenState = @GetFlexState(4)
                cPos = math.floor(timeOfAnim * 20) + 1
                data = @talkAnim[cPos]
                return if not data
                {jaw, out, up, down} = data
                volume = @ent\VoiceVolume() * 6
                jaw *= volume
                out *= volume
                up *= volume
                down *= volume
                JawOpenState\SetModifierWeight(JawOpen, jaw)
                Tongue_OutOpenState\SetModifierWeight(Tongue_OutOpen, out)
                Tongue_UpOpenState\SetModifierWeight(Tongue_UpOpen, up)
                Tongue_DownOpenState\SetModifierWeight(Tongue_DownOpen, down)
        }

        {
            'name': 'eyes_blink'
            'autostart': true
            'repeat': true
            'time': 7
            'ids': {'Left_Blink', 'Right_Blink'}
            'create': =>
                @nextBlink = math.random(300, 600) / 1000
                @nextBlinkLength = math.random(50, 75) / 1000
                @min, @max = @nextBlink, @nextBlink + @nextBlinkLength
            'func': (delta, timeOfAnim) =>
                if @min > timeOfAnim or @max < timeOfAnim
                    if @blinkHit
                        @blinkHit = false
                        left, right = @GetModifierID(1), @GetModifierID(2)
                        leftState, rightState = @GetFlexState(1), @GetFlexState(2)
                        leftState\SetModifierWeight(left, 0)
                        rightState\SetModifierWeight(right, 0)
                    return
                left, right = @GetModifierID(1), @GetModifierID(2)
                leftState, rightState = @GetFlexState(1), @GetFlexState(2)
                leftState\SetModifierWeight(left, .9)
                rightState\SetModifierWeight(right, .9)
                @blinkHit = true
        }
    }

    @__inherited: (child) =>
        child.MODELS_HASH = {mod, true for mod in *child.MODELS}
        @AVALIABLE_CONTROLLERS[mod] = child for mod in *child.MODELS
        for i, flex in pairs child.FLEX_LIST
            flex.id = i - 1
            flex.targetName = "target#{flex.flex}"
        child.FLEX_IDS = {flex.id, flex for flex in *child.FLEX_LIST}
        child.FLEX_TABLE = {flex.flex, flex for flex in *child.FLEX_LIST}
        seq.numid = i for i, seq in pairs child.FLEX_SEQUENCES
        child.FLEX_SEQUENCES_TABLE = {seq.name, seq for seq in *child.FLEX_SEQUENCES}
        child.FLEX_SEQUENCES_TABLE[seq.numid] = seq for seq in *child.FLEX_SEQUENCES
    @__inherited(@)

    @NEXT_HOOK_ID = 0

    new: (data) =>
        @controller = data
        @ent = data.ent
        @states = [FlexState(@, flex, id, scale, speed) for {:flex, :id, :scale, :speed} in *@@FLEX_LIST]
        @statesTable = {state\GetFlexName(), state for state in *@states}
        @statesTable[state\GetFlexName()\lower()] = state for state in *@states
        @statesTable[state\GetFlexID()] = state for state in *@states
        @hooks = {}
        @@NEXT_HOOK_ID += 1
        @fid = @@NEXT_HOOK_ID
        @hookID = "PPM2.FlexController.#{@@NEXT_HOOK_ID}"
        @lastThink = RealTime()
        @currentSequences = {}
        @currentSequencesIterable = {}
        @ResetSequences()
        @Hook('OnPlayerChat', @OnPlayerChat)
        @Hook('PlayerStartVoice', @PlayerStartVoice)
        @Hook('PlayerEndVoice', @PlayerEndVoice)

    StartSequence: (seqID = '') =>
        return @currentSequences[seqID] if @currentSequences[seqID]
        return if not @@FLEX_SEQUENCES_TABLE[seqID]
        @currentSequences[seqID] = FlexSequence(@, @@FLEX_SEQUENCES_TABLE[seqID])
        @currentSequencesIterable = [seq for i, seq in pairs @currentSequences]
        return @currentSequences[seqID]

    EndSequence: (seqID = '', callStop = true) =>
        return false if not @currentSequences[seqID]
        @currentSequences[seqID]\Stop() if callStop
        @currentSequences[seqID] = nil
        @currentSequencesIterable = [seq for i, seq in pairs @currentSequences]
        return true

    ResetSequences: =>
        for seq in *@currentSequencesIterable
            seq\Stop()

        @currentSequences = {}
        @currentSequencesIterable = {}
        state\Think(1000) for state in *@states

        for seq in *@@FLEX_SEQUENCES
            continue if not seq.autostart
            @StartSequence(seq.name)

    PlayerRespawn: =>
        @ResetSequences()

    HasSequence: (seqID = '') => @currentSequences[seqID] and true or false

    GetFlexState: (name = '') => @statesTable[name]
    DataChanges: (state) =>
    GetEntity: => @ent
    GetData: => @controller
    GetController: => @controller

    Hook: (id, func) =>
        newFunc = (...) ->
            if not IsValid(@ent)
                @ent = @GetData().ent
            if not IsValid(@ent) or @GetData()\GetData() ~= @ent\GetPonyData()
                @RemoveHooks()
                return
            func(@, ...)
            return nil
        hook.Add id, @hookID, newFunc
        table.insert(@hooks, id)

    OnPlayerChat: (ply = NULL, text = '', teamOnly = false, isDead = false) =>
        return if ply ~= @ent or teamOnly or isDead
        switch text\lower()
            when 'o'
                @StartSequence('ooo')
            when ':o'
                @StartSequence('ooo')
            when 'о'
                @StartSequence('ooo')
            when 'О'
                @StartSequence('ooo')
            when ':о'
                @StartSequence('ooo')
            when ':О'
                @StartSequence('ooo')
            when ':3'
                @StartSequence('cat')
            when ':з'
                @StartSequence('cat')
            when ':d'
                @StartSequence('big_grin')
            when 'xd'
                @StartSequence('xd')
            when 'exdi'
                @StartSequence('xd')
            when ':p'
                @StartSequence('tongue')
            when ':р'
                @StartSequence('tongue')
            when ':Р'
                @StartSequence('tongue')
            when ':c'
                @StartSequence('sad')
            when ':с'
                @StartSequence('sad')
            when ':С'
                @StartSequence('sad')
            else
                if string.find(text, 'hehehe') or string.find(text, 'hahaha')
                    @StartSequence('greeny')
                else
                    @StartSequence('talk')
    PlayerStartVoice: (ply = NULL) =>
        return if ply ~= @ent
        @StartSequence('talk_endless')
    PlayerEndVoice: (ply = NULL) =>
        return if ply ~= @ent
        @EndSequence('talk_endless')

    RemoveHooks: =>
        for iHook in *@hooks
            hook.Remove iHook, @hookID

    Think: =>
        return if DISABLE_FLEXES\GetBool()
        if not IsValid(@ent)
            @ent = @GetData().ent
        return if not IsValid(@ent) or @ent\IsDormant() or not @ent\Alive()
        delta = RealTime() - @lastThink
        @lastThink = RealTime()
        state\Think(delta) for state in *@states
        for seq in *@currentSequencesIterable
            if not seq\IsValid()
                @EndSequence(seq\GetName(), false)
                break
            seq\Think(delta)

do
    ppm2_disable_flexes = (cvar, oldval, newval) ->
        for ply in *player.GetAll()
            data = ply\GetPonyData()
            continue if not data
            flex = data\GetFlexController()
            continue if not flex
            flex\ResetSequences()
    cvars.AddChangeCallback 'ppm2_disable_flexes', ppm2_disable_flexes, 'ppm2_disable_flexes'

PPM2.PonyFlexController = PonyFlexController
PPM2.GetFlexController = (model = 'models/ppm/player_default_base_new.mdl') -> PonyFlexController.AVALIABLE_CONTROLLERS[model]


Probably fixed weight

Probably fixed weight

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

-- it is defined shared, but used clientside only

import PPM2 from _G

-- 0	LrigPelvis
-- 1	LrigSpine1
-- 2	LrigSpine2
-- 3	LrigRibcage
-- 4	LrigNeck1
-- 5	LrigNeck2
-- 6	LrigNeck3
-- 7	LrigScull
-- 8	Lrig_LEG_BL_Femur
-- 9	Lrig_LEG_BL_Tibia
-- 10	Lrig_LEG_BL_LargeCannon
-- 11	Lrig_LEG_BL_PhalanxPrima
-- 12	Lrig_LEG_BL_RearHoof
-- 13	Lrig_LEG_BR_Femur
-- 14	Lrig_LEG_BR_Tibia
-- 15	Lrig_LEG_BR_LargeCannon
-- 16	Lrig_LEG_BR_PhalanxPrima
-- 17	Lrig_LEG_BR_RearHoof
-- 18	Lrig_LEG_FL_Scapula
-- 19	Lrig_LEG_FL_Humerus
-- 20	Lrig_LEG_FL_Radius
-- 21	Lrig_LEG_FL_Metacarpus
-- 22	Lrig_LEG_FL_PhalangesManus
-- 23	Lrig_LEG_FL_FrontHoof
-- 24	Lrig_LEG_FR_Scapula
-- 25	Lrig_LEG_FR_Humerus
-- 26	Lrig_LEG_FR_Radius
-- 27	Lrig_LEG_FR_Metacarpus
-- 28	Lrig_LEG_FR_PhalangesManus
-- 29	Lrig_LEG_FR_FrontHoof
-- 30	Mane01
-- 31	Mane02
-- 32	Mane03
-- 33	Mane04
-- 34	Mane05
-- 35	Mane06
-- 36	Mane07
-- 37	Mane03_tip
-- 38	Tail01
-- 39	Tail02
-- 40	Tail03

class PonyWeightController extends PPM2.ControllerChildren
	@AVALIABLE_CONTROLLERS = {}
	@MODELS = {'models/ppm/player_default_base.mdl', 'models/ppm/player_default_base_nj.mdl', 'models/cppm/player_default_base.mdl', 'models/cppm/player_default_base_nj.mdl'}
	@HARD_LIMIT_MINIMAL = 0.1
	@HARD_LIMIT_MAXIMAL = 3

	@DEFAULT_BONE_SIZE = Vector(1, 1, 1)
	@NEXT_OBJ_ID = 0

	new: (data, applyWeight = true) =>
		@isValid = true
		@networkedData = data
		@ent = data.ent
		@objID = @@NEXT_OBJ_ID
		@@NEXT_OBJ_ID += 1
		@lastPAC3BoneReset = 0
		@SetWeight(data\GetWeight())
		@UpdateWeight() if IsValid(@ent) and applyWeight
		PPM2.DebugPrint('Created new weight controller for ', @ent, ' as part of ', data, '; internal ID is ', @objID)

	__tostring: => "[#{@@__name}:#{@objID}|#{@GetData()}]"
	IsValid: => IsValid(@ent) and @isValid
	GetEntity: => @ent
	GetData: => @networkedData
	GetController: => @networkedData
	GetModel: => @networkedData\GetModel()

	@WEIGHT_BONES = {
		{id: 0, scale: 1.1}
		{id: 1, scale: 0.7}
		{id: 2, scale: 0.7}
		{id: 3, scale: 0.7}
	}

	table.insert(@WEIGHT_BONES, {id: i, scale: 1}) for i = 8, 29

	DataChanges: (state) =>
		return if not IsValid(ent) or not @isValid
		return if state\GetKey() ~= 'Weight'
		@SetWeight(state\GetValue())
		@UpdateWeight()

	SetWeight: (weight = 1) => @weight = math.Clamp(weight, @@HARD_LIMIT_MINIMAL, @@HARD_LIMIT_MAXIMAL)
	SlowUpdate: =>

	ResetBones: (ent = @ent) =>
		return if not IsValid(ent) or not @isValid
		for {:id} in *@@WEIGHT_BONES
			ent\ManipulateBoneScale(id, @@DEFAULT_BONE_SIZE)

	Reset: => @ResetBones()

	UpdateWeight: (ent = @ent) =>
		return if not IsValid(ent) or not @isValid
		return if not @ent\IsPony()

		for {:id, :scale} in *@@WEIGHT_BONES
			delta = 1 + (@weight - 1) * scale
			ent\ManipulateBoneScale(id, Vector(delta, delta, delta))

	Remove: =>
		@isValid = false

if CLIENT
	reset = (ent, data) ->
		if weight = data\GetWeightController()
			weight.ent = ent
			weight\UpdateWeight()

	hook.Add 'PPM2.SetupBones', 'PPM2.Weight', reset, -2

--
-- 0	LrigPelvis
-- 1	Lrig_LEG_BL_Femur
-- 2	Lrig_LEG_BL_Tibia
-- 3	Lrig_LEG_BL_LargeCannon
-- 4	Lrig_LEG_BL_PhalanxPrima
-- 5	Lrig_LEG_BL_RearHoof
-- 6	Lrig_LEG_BR_Femur
-- 7	Lrig_LEG_BR_Tibia
-- 8	Lrig_LEG_BR_LargeCannon
-- 9	Lrig_LEG_BR_PhalanxPrima
-- 10	Lrig_LEG_BR_RearHoof
-- 11	LrigSpine1
-- 12	LrigSpine2
-- 13	LrigRibcage
-- 14	Lrig_LEG_FL_Scapula
-- 15	Lrig_LEG_FL_Humerus
-- 16	Lrig_LEG_FL_Radius
-- 17	Lrig_LEG_FL_Metacarpus
-- 18	Lrig_LEG_FL_PhalangesManus
-- 19	Lrig_LEG_FL_FrontHoof
-- 20	Lrig_LEG_FR_Scapula
-- 21	Lrig_LEG_FR_Humerus
-- 22	Lrig_LEG_FR_Radius
-- 23	Lrig_LEG_FR_Metacarpus
-- 24	Lrig_LEG_FR_PhalangesManus
-- 25	Lrig_LEG_FR_FrontHoof
-- 26	LrigNeck1
-- 27	LrigNeck2
-- 28	LrigNeck3
-- 29	LrigScull
-- 30	Jaw
-- 31	Ear_L
-- 32	Ear_R
-- 33	Mane02
-- 34	Mane03
-- 35	Mane03_tip
-- 36	Mane04
-- 37	Mane05
-- 38	Mane06
-- 39	Mane07
-- 40	Mane01
-- 41	Lrigweaponbone
-- 42	Tail01
-- 43	Tail02
-- 44	Tail03

class NewPonyWeightController extends PonyWeightController
	@MODELS = {'models/ppm/player_default_base_new.mdl', 'models/ppm/player_default_base_new_nj.mdl'}

	__tostring: => "[#{@@__name}:#{@objID}|#{@GetData()}]"

	@WEIGHT_BONES = {
		{id: 0, scale: 1.1}
		{id: 1, scale: 0.7}
		{id: 6, scale: 0.7}
		{id: 11, scale: 0.7}
		{id: 12, scale: 0.7}
		{id: 13, scale: 0.7}
		{id: 14, scale: 0.7}
		{id: 20, scale: 0.7}

		{id: 5, scale: 0.9}
		{id: 10, scale: 0.9}
		{id: 19, scale: 0.9}
		{id: 25, scale: 0.9}
	}

	table.insert(@WEIGHT_BONES, {id: i, scale: 1}) for i = 1, 10
	table.insert(@WEIGHT_BONES, {id: i, scale: 1}) for i = 14, 28

PPM2.PonyWeightController = PonyWeightController
PPM2.NewPonyWeightController = NewPonyWeightController
PPM2.GetPonyWeightController = (model = '') -> PonyWeightController.AVALIABLE_CONTROLLERS[model] or PonyWeightController

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

-- it is defined shared, but used clientside only

import PPM2 from _G
import ManipulateBoneScale, GetManipulateBoneScale from FindMetaTable('Entity')

-- 0	LrigPelvis
-- 1	LrigSpine1
-- 2	LrigSpine2
-- 3	LrigRibcage
-- 4	LrigNeck1
-- 5	LrigNeck2
-- 6	LrigNeck3
-- 7	LrigScull
-- 8	Lrig_LEG_BL_Femur
-- 9	Lrig_LEG_BL_Tibia
-- 10	Lrig_LEG_BL_LargeCannon
-- 11	Lrig_LEG_BL_PhalanxPrima
-- 12	Lrig_LEG_BL_RearHoof
-- 13	Lrig_LEG_BR_Femur
-- 14	Lrig_LEG_BR_Tibia
-- 15	Lrig_LEG_BR_LargeCannon
-- 16	Lrig_LEG_BR_PhalanxPrima
-- 17	Lrig_LEG_BR_RearHoof
-- 18	Lrig_LEG_FL_Scapula
-- 19	Lrig_LEG_FL_Humerus
-- 20	Lrig_LEG_FL_Radius
-- 21	Lrig_LEG_FL_Metacarpus
-- 22	Lrig_LEG_FL_PhalangesManus
-- 23	Lrig_LEG_FL_FrontHoof
-- 24	Lrig_LEG_FR_Scapula
-- 25	Lrig_LEG_FR_Humerus
-- 26	Lrig_LEG_FR_Radius
-- 27	Lrig_LEG_FR_Metacarpus
-- 28	Lrig_LEG_FR_PhalangesManus
-- 29	Lrig_LEG_FR_FrontHoof
-- 30	Mane01
-- 31	Mane02
-- 32	Mane03
-- 33	Mane04
-- 34	Mane05
-- 35	Mane06
-- 36	Mane07
-- 37	Mane03_tip
-- 38	Tail01
-- 39	Tail02
-- 40	Tail03

class PonyWeightController extends PPM2.ControllerChildren
	@AVALIABLE_CONTROLLERS = {}
	@MODELS = {'models/ppm/player_default_base.mdl', 'models/ppm/player_default_base_nj.mdl', 'models/cppm/player_default_base.mdl', 'models/cppm/player_default_base_nj.mdl'}
	@HARD_LIMIT_MINIMAL = 0.1
	@HARD_LIMIT_MAXIMAL = 3

	@NEXT_OBJ_ID = 0

	new: (data, applyWeight = true) =>
		@isValid = true
		@networkedData = data
		@ent = data.ent
		@objID = @@NEXT_OBJ_ID
		@@NEXT_OBJ_ID += 1
		@lastPAC3BoneReset = 0
		@SetWeight(data\GetWeight())
		@UpdateWeight() if IsValid(@ent) and applyWeight
		PPM2.DebugPrint('Created new weight controller for ', @ent, ' as part of ', data, '; internal ID is ', @objID)

	__tostring: => "[#{@@__name}:#{@objID}|#{@GetData()}]"
	IsValid: => IsValid(@ent) and @isValid
	GetEntity: => @ent
	GetData: => @networkedData
	GetController: => @networkedData
	GetModel: => @networkedData\GetModel()

	@WEIGHT_BONES = {
		{id: 0, scale: 0.7}
		{id: 1, scale: 0.7}
		{id: 2, scale: 0.7}
		{id: 3, scale: 0.7}
	}

	table.insert(@WEIGHT_BONES, {id: i, scale: 1}) for i = 8, 29

	DataChanges: (state) =>
		return if not IsValid(ent) or not @isValid
		return if state\GetKey() ~= 'Weight'
		@SetWeight(state\GetValue())
		@UpdateWeight()

	SetWeight: (weight = 1) => @weight = math.Clamp(weight, @@HARD_LIMIT_MINIMAL, @@HARD_LIMIT_MAXIMAL)
	SlowUpdate: => @UpdateWeight() if @lastPAC3BoneReset < RealTime()

	@DEFAULT_BONE_SIZE = Vector(1, 1, 1)
	ResetBones: (ent = @ent) =>
		return if not IsValid(ent) or not @isValid
		ent = ent\GetEntity()
		for {:id} in *@@WEIGHT_BONES
			ManipulateBoneScale(ent, id, @@DEFAULT_BONE_SIZE)
	Reset: => @ResetBones()
	UpdateWeight: (ent = @ent) =>
		return if not IsValid(ent) or not @isValid
		ent = ent\GetEntity()
		@ResetBones(ent)
		return if not @ent\IsPony()
		for {:id, :scale} in *@@WEIGHT_BONES
			delta = (@weight - 1) * scale
			ManipulateBoneScale(ent, id, Vector(delta, delta, delta) + GetManipulateBoneScale(ent, id))
	Remove: =>
		@isValid = false

if CLIENT
	hook.Add 'PPM2.SetupBones', 'PPM2.Weight', (ent, data) ->
		if weight = data\GetWeightController()
			weight.ent = ent
			weight.lastPAC3BoneReset = RealTime() + 1
			weight\UpdateWeight()
--
-- 0	LrigPelvis
-- 1	Lrig_LEG_BL_Femur
-- 2	Lrig_LEG_BL_Tibia
-- 3	Lrig_LEG_BL_LargeCannon
-- 4	Lrig_LEG_BL_PhalanxPrima
-- 5	Lrig_LEG_BL_RearHoof
-- 6	Lrig_LEG_BR_Femur
-- 7	Lrig_LEG_BR_Tibia
-- 8	Lrig_LEG_BR_LargeCannon
-- 9	Lrig_LEG_BR_PhalanxPrima
-- 10	Lrig_LEG_BR_RearHoof
-- 11	LrigSpine1
-- 12	LrigSpine2
-- 13	LrigRibcage
-- 14	Lrig_LEG_FL_Scapula
-- 15	Lrig_LEG_FL_Humerus
-- 16	Lrig_LEG_FL_Radius
-- 17	Lrig_LEG_FL_Metacarpus
-- 18	Lrig_LEG_FL_PhalangesManus
-- 19	Lrig_LEG_FL_FrontHoof
-- 20	Lrig_LEG_FR_Scapula
-- 21	Lrig_LEG_FR_Humerus
-- 22	Lrig_LEG_FR_Radius
-- 23	Lrig_LEG_FR_Metacarpus
-- 24	Lrig_LEG_FR_PhalangesManus
-- 25	Lrig_LEG_FR_FrontHoof
-- 26	LrigNeck1
-- 27	LrigNeck2
-- 28	LrigNeck3
-- 29	LrigScull
-- 30	Jaw
-- 31	Ear_L
-- 32	Ear_R
-- 33	Mane02
-- 34	Mane03
-- 35	Mane03_tip
-- 36	Mane04
-- 37	Mane05
-- 38	Mane06
-- 39	Mane07
-- 40	Mane01
-- 41	Lrigweaponbone
-- 42	Tail01
-- 43	Tail02
-- 44	Tail03

class NewPonyWeightController extends PonyWeightController
	@MODELS = {'models/ppm/player_default_base_new.mdl', 'models/ppm/player_default_base_new_nj.mdl'}

	__tostring: => "[#{@@__name}:#{@objID}|#{@GetData()}]"

	@WEIGHT_BONES = {
		{id: 0, scale: 0.7}
		{id: 1, scale: 0.7}
		{id: 6, scale: 0.7}
		{id: 11, scale: 0.7}
		{id: 12, scale: 0.7}
		{id: 13, scale: 0.7}
		{id: 14, scale: 0.7}
		{id: 20, scale: 0.7}
	}

	table.insert(@WEIGHT_BONES, {id: i, scale: 1}) for i = 1, 10
	table.insert(@WEIGHT_BONES, {id: i, scale: 1}) for i = 14, 28

PPM2.PonyWeightController = PonyWeightController
PPM2.NewPonyWeightController = NewPonyWeightController
PPM2.GetPonyWeightController = (model = '') -> PonyWeightController.AVALIABLE_CONTROLLERS[model] or PonyWeightController