Started adding engine, but.... I don't think I'm going to go this route.

Class = require('lib/class')
EventManager = require('lib/event-manager')

class Engine extends Class

	new: () =>
		@entities = {}
		@singleRequirements = {}
		@allRequirements = {}
		@entityLists = {}
		@events = EventManager()

		@systemRegistry = {}
		@systems = {}
		@systems.update = {}
		@systems.draw = {}

		@events\addListener('entity.component.added', @, @onComponentAdded)
		@events\addListener('entity.component.removed', @, @onComponentRemoved)

	addEntity: (entity) =>
		entity.events = @events
		entity.id = #@entities + 1
		@entities[entity.id] = entity

		for key, component in pairs(entity.components)
			if not @entityLists[key] then @entityLists[name] = {}
			@entityLists[key][entity.id] = entity

			if @singleRequirements[key]
				for _, system in pairs(@singleRequirements[key])
					if @checkRequirements(entity, system)

	removeEntity: (entity) =>

	addSystem: (entity) =>

{ Engine: ToysEngine } = require('vendor/lovetoys/lovetoys')
ToysProxy = require('lib/utils/shims/lovetoys-proxy')

class Engine extends ToysProxy(ToysEngine)


weatherunderground: change language, remove dummy

Former-commit-id: 07fa2db17b094df57a45a5037cd0d180f44a1ec1 [formerly 266c8c538b01830c3149aa7aacc61468b393a7f2]
Former-commit-id: ad119fe4fdc32928c6024e82bdf48ca2baea4d94

-- Weather Underground API module, requires a key set in configuration
-- WeatherUndergroundKey

util = require'util'
os = require'os'
iconv = require'iconv'

iso2utf = iconv.new('latin1', 'utf-8')

key = ivar2.config.WeatherUndergroundKey
unless key
  return {}

geoApiBase = 'http://api.geonames.org/searchJSON?name=%s&featureClass=P&username=haste'
language = 'EN'

tempColor = (v) ->
  v = tonumber(v)
  if v > 0
    color = util.red
    return color(v)
  else
    color = util.lightblue
    return color(v)

windSpeedToName = (ws) ->
  windtext = ''
  unless ws
    return windtext
  if ws > 32
    windtext = 'orkan!'
  elseif ws > 28.5
    windtext = 'sterk storm'
  elseif ws > 24.5
    windtext = 'full storm'
  elseif ws > 20.8
    windtext = 'liten storm'
  elseif ws > 17.2
    windtext = 'sterk kuling'
  elseif ws > 13.9
    windtext = 'stiv kuling'
  elseif ws > 10.8
    windtext = 'liten kuling'
  elseif ws > 8
    windtext = 'frisk bris'
  elseif ws > 5.5
    windtext = 'laber bris'
  elseif ws > 3.4
    windtext = 'lett bris'
  elseif ws > 1.5
    windtext = 'svak vind'
  elseif ws > 0.3
    windtext = 'flau vind'
  else
    windtext = 'vindstille'
  return windtext

findLocation = (source, destination, name, cb) =>
  gurl = geoApiBase\format(util.urlEncode(name))
  util.simplehttp gurl, (data) ->
    json = util.json.decode(data)
    if json.totalResultsCount == 0
      return @Msg('privmsg', destination, source, "Does that place even exist?")
    city = json.geonames[1]
    lat = city.lat
    lon = city.lng
    cb(lon, lat)

lookupConditions = (source, destination, input, pws) =>
  findLocation @, source, destination, input, (lon, lat) ->
    pws = pws or false
    if pws
      pws = 'pws:1/'
    else
      pws = 'pws:0/'
    url = "http://api.wunderground.com/api/#{key}/lang:#{language}/#{pws}conditions/q/#{lat},#{lon}.json"
    @Log 'debug', "Fetching WU URL: %s", url
    util.simplehttp url, (data) ->
      json = util.json.decode(data)
      if json.error ~= nil and json.error.description ~=nil
        return say json.error.description
      current = json.current_observation

      location = current.observation_location
      city = iso2utf\iconv location.city

      weather = current.weather
      temp = current.temp_c
      feelsLike = current.feelslike_c

      windSpeedname = windSpeedToName(current.wind_kph/3.6)

      windDirection = current.wind_dir

      rain = current.precip_today_metric

      time = os.date('%H:%M', tonumber(current.observation_epoch))

      @Msg 'privmsg', destination, source, '%s, %s °C (feels like %s °C), %s %s, %s mm, (%s, %s)', weather, tempColor(temp), tempColor(feelsLike), windDirection, windSpeedname, rain, city, time

lookupConditionsPWS = (source, destination, input) =>
  lookupConditions(@, source, destination, input, true)

PRIVMSG:
  '^%pwu (.+)$': lookupConditions
  '^%ppws (.+)$': lookupConditionsPWS

-- Weather Underground API module, requires a key set in configuration
-- WeatherUndergroundKey

util = require'util'
os = require'os'
iconv = require'iconv'

iso2utf = iconv.new('latin1', 'utf-8')

key = ivar2.config.WeatherUndergroundKey
unless key
  return {}

geoApiBase = 'http://api.geonames.org/searchJSON?name=%s&featureClass=P&username=haste'
wugeolookupurl = 'http://api.wunderground.com/api/%s/geolookup/q/59.788,5.7218.json'
language = 'NO'

tempColor = (v) ->
  v = tonumber(v)
  if v > 0
    color = util.red
    return color(v)
  else
    color = util.lightblue
    return color(v)

windSpeedToName = (ws) ->
  windtext = ''
  unless ws
    return windtext
  if ws > 32
    windtext = 'orkan!'
  elseif ws > 28.5
    windtext = 'sterk storm'
  elseif ws > 24.5
    windtext = 'full storm'
  elseif ws > 20.8
    windtext = 'liten storm'
  elseif ws > 17.2
    windtext = 'sterk kuling'
  elseif ws > 13.9
    windtext = 'stiv kuling'
  elseif ws > 10.8
    windtext = 'liten kuling'
  elseif ws > 8
    windtext = 'frisk bris'
  elseif ws > 5.5
    windtext = 'laber bris'
  elseif ws > 3.4
    windtext = 'lett bris'
  elseif ws > 1.5
    windtext = 'svak vind'
  elseif ws > 0.3
    windtext = 'flau vind'
  else
    windtext = 'vindstille'
  return windtext

findLocation = (source, destination, name, cb) =>
  gurl = geoApiBase\format(util.urlEncode(name))
  util.simplehttp gurl, (data) ->
    json = util.json.decode(data)
    if json.totalResultsCount == 0
      return @Msg('privmsg', destination, source, "Does that place even exist?")
    city = json.geonames[1]
    lat = city.lat
    lon = city.lng
    cb(lon, lat)

lookupConditions = (source, destination, input, pws) =>
  findLocation @, source, destination, input, (lon, lat) ->
    pws = pws or false
    if pws
      pws = 'pws:1/'
    else
      pws = 'pws:0/'
    url = "http://api.wunderground.com/api/#{key}/lang:#{language}/#{pws}conditions/q/#{lat},#{lon}.json"
    @Log 'debug', "Fetching WU URL: %s", url
    util.simplehttp url, (data) ->
      json = util.json.decode(data)
      if json.error ~= nil and json.error.description ~=nil
        return say json.error.description
      current = json.current_observation

      location = current.observation_location
      city = iso2utf\iconv location.city

      weather = current.weather
      temp = current.temp_c
      feelsLike = current.feelslike_c

      windSpeedname = windSpeedToName(current.wind_kph/3.6)

      windDirection = current.wind_dir

      rain = current.precip_today_metric

      time = os.date('%H:%M', tonumber(current.observation_epoch))

      @Msg 'privmsg', destination, source, '%s, %s °C (følt %s °C), %s %s, %s mm, (%s, %s)', weather, tempColor(temp), tempColor(feelsLike), windDirection, windSpeedname, rain, city, time

lookupConditionsPWS = (source, destination, input) =>
  lookupConditions(@, source, destination, input, true)

PRIVMSG:
  '^%pwu (.+)$': lookupConditions
  '^%ppws (.+)$': lookupConditionsPWS


added ability to pause

added ability to pause

require("lib/lgm/lgm")
require("wall")
require("player")

export wScr, hScr
wScr = love.window.getWidth
hScr = love.window.getHeight

kPAUSE = false

love.load = ->

    arenaOutSize = 40

    colorP1 = {227,138,23}
    colorP2 = {91,205,229}

    useSound = true

    bgm_main = love.audio.newSource("res/bgm_main.ogg", "stream")
    bgm_main\setVolume(0.5)
    sfx_turn = {}
    for i=1,10
        s = love.audio.newSource("res/sfx_turn.wav", "static")
        s\setVolume(0.5)
        s\setPitch(0.5 + 0.1 * i)
        table.insert(sfx_turn, s)

    wallSet\add(Wall arenaOutSize, arenaOutSize, wScr() - arenaOutSize,
        arenaOutSize,
        {255,255,255},
        -1)
    wallSet\add(Wall arenaOutSize,
        arenaOutSize,
        arenaOutSize,
        hScr() - arenaOutSize,
        {255,255,255},
        -1)
    wallSet\add(Wall wScr() - arenaOutSize,
        arenaOutSize,
        wScr() - arenaOutSize,
        hScr() - arenaOutSize,
        {255,255,255},
        -1)
    wallSet\add(Wall arenaOutSize,
        hScr() - arenaOutSize,
        wScr() - arenaOutSize,
        hScr() - arenaOutSize,
        {255,255,255},
        -1)

    playerSet\add(Player wScr() - (arenaOutSize + 10),
        hScr() - (arenaOutSize + 10),
        -1, 0,colorP1)
    playerSet\add(Player arenaOutSize + 10, arenaOutSize + 10, 1, 0, colorP2)

    bgm_main\play()

    love.update = (dt) ->
        if not kPAUSE
            for wall in wallSet\iter()
                wall\update(dt)
                if (wall.time > wall.lifeTime) then
                    wallSet\removeID(wall.id)
            for player in playerSet\iter()
                player\update(dt)

    love.draw = ->
        for player in playerSet\iter()
            player\draw()
            love.graphics.setColor(player.color)
            love.graphics.print(player.score, 50 + 30 * player.id , 10)

        for wall in wallSet\iter()
            wall\draw()

        -- music icon ♪
        if useSound
            love.graphics.setColor(255,255,255)
            love.graphics.print("m to mute", 1, 1)

    love.keypressed = (k) ->
        switch k
            when "up", "right","left", "down"
                playerSet\find(1)\keypressed(k)
            when "w", "z"
                playerSet\find(2)\keypressed("up")
            when "d"
                playerSet\find(2)\keypressed("right")
            when "s"
                playerSet\find(2)\keypressed("down")
            when "q", "a"
                playerSet\find(2)\keypressed("left")

            when "p"
                kPAUSE = not kPAUSE

            when "m"
                useSound = not useSound
                curV = love.audio.getVolume()
                love.audio.setVolume(1.0 - curV)

            when "escape"
                love.event.push("quit")

    export playSfxTurn = ->
        i = -1
        while i == -1 or not sfx_turn[i]\isStopped()
            i = love.math.random(1, #sfx_turn)
        sfx_turn[i]\play()

require("lib/lgm/lgm")
require("wall")
require("player")

export wScr, hScr
wScr = love.window.getWidth
hScr = love.window.getHeight

love.load = ->

    arenaOutSize = 40

    colorP1 = {227,138,23}
    colorP2 = {91,205,229}

    useSound = true

    bgm_main = love.audio.newSource("res/bgm_main.ogg", "stream")
    bgm_main\setVolume(0.5)
    sfx_turn = {}
    for i=1,10
        s = love.audio.newSource("res/sfx_turn.wav", "static")
        s\setVolume(0.5)
        s\setPitch(0.5 + 0.1 * i)
        table.insert(sfx_turn, s)

    wallSet\add(Wall arenaOutSize, arenaOutSize, wScr() - arenaOutSize,
        arenaOutSize,
        {255,255,255},
        -1)
    wallSet\add(Wall arenaOutSize,
        arenaOutSize,
        arenaOutSize,
        hScr() - arenaOutSize,
        {255,255,255},
        -1)
    wallSet\add(Wall wScr() - arenaOutSize,
        arenaOutSize,
        wScr() - arenaOutSize,
        hScr() - arenaOutSize,
        {255,255,255},
        -1)
    wallSet\add(Wall arenaOutSize,
        hScr() - arenaOutSize,
        wScr() - arenaOutSize,
        hScr() - arenaOutSize,
        {255,255,255},
        -1)

    playerSet\add(Player wScr() - (arenaOutSize + 10),
        hScr() - (arenaOutSize + 10),
        -1, 0,colorP1)
    playerSet\add(Player arenaOutSize + 10, arenaOutSize + 10, 1, 0, colorP2)

    bgm_main\play()

    love.update = (dt) ->
        for wall in wallSet\iter()
            wall\update(dt)
            if (wall.time > wall.lifeTime) then
                wallSet\removeID(wall.id)
        for player in playerSet\iter()
            player\update(dt)

    love.draw = ->
        for player in playerSet\iter()
            player\draw()
            love.graphics.setColor(player.color)
            love.graphics.print(player.score, 50 + 30 * player.id , 10)

        for wall in wallSet\iter()
            wall\draw()

        -- music icon ♪
        if useSound
            love.graphics.setColor(255,255,255)
            love.graphics.print("m to mute", 1, 1)

    love.keypressed = (k) ->
        switch k
            when "up", "right","left", "down"
                playerSet\find(1)\keypressed(k)
            when "w", "z"
                playerSet\find(2)\keypressed("up")
            when "d"
                playerSet\find(2)\keypressed("right")
            when "s"
                playerSet\find(2)\keypressed("down")
            when "q", "a"
                playerSet\find(2)\keypressed("left")

            when "m"
                useSound = not useSound
                curV = love.audio.getVolume()
                love.audio.setVolume(1.0 - curV)

            when "escape"
                love.event.push("quit")

    export playSfxTurn = ->
        i = -1
        while i == -1 or not sfx_turn[i]\isStopped()
            i = love.math.random(1, #sfx_turn)
        sfx_turn[i]\play()


imageupload: store name, fix encoding issue

{:simplehttp, :json, :urlEncode} = require'util'
html2unicode = require 'html'
nixio = require'nixio'
fs = require'nixio.fs'
ltn12 = require'ltn12'

urlbase = '/image/'

safe = (fn) ->
  f, ext = fn\match'^(.*)%.(.-)$'
  f = f\gsub '[^%w%-]', ''
  return f..'.'..ext

ivar2.webserver.regUrl "#{urlbase}(.*)$", (req, res) ->
  send = (body, code, content_type) ->
    if not code then code = 200
    if not content_type then content_type = 'text/html'
    res\set_status code
    res\set_header 'Content-Type', content_type
    res\set_header 'Content-Length', #body
    res\set_body body
    res\send!
    res.on_error = (res, req, err) ->
      ivar2\Log 'error', 'imageupload: error: %s', err

  file = req.url\match '/file/(.*)$'
  if file
    fn = "cache/images/#{safe file}"
    size = nixio.fs.stat(fn).size
    fd = ltn12.source.file(io.open fn, 'rb')
    --body = fd\read '*a'
    --fd\close!
    content_type = 'image/jpeg'
    if file\match '.png'
      content_type = 'image/png'
    if file\match '.svg'
      content_type = 'image/svg'
    if file\match '.mp4'
      content_type = 'video/mp4'
    if file\match '.mp3'
      content_type = 'audio/mp3'

    res\set_status code
    res\set_header 'Content-Type', content_type
    res\set_header 'Content-Length', size
    res\set_body fd
    res\send!
    ivar2\Log 'error', 'imageupload: serving: %s', fn

    return

  channel = req.url\match('channel=(.+)')
  unless channel
    send 'Invalid channel', 404
    return

  channel = html2unicode channel
  unescaped_channel = channel\gsub '%%23', '#'

  html = [[
  <!DOCTYPE html>

  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
  <meta name="mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="apple-mobile-web-app-title" content="IRCSNAP ]]..unescaped_channel..[[">
  <meta name="theme-color" content="#8f4099">

  <title>IRCSNAP</title>

Share to IRC app - ]]..unescaped_channel..[[ edition

Fill in your nick/name and optional text and then click on one of the two yellow buttons to attach image. It will appear instantly on IRC!

    <form>

        <input id="sender" type="text" required>

        Your name

        <input id="text" type="text" required>

        Attach text

    </form>

Uploading. Percent complete: 0 %

      📷 Snap picture! <input type="file" accept="image/*" id="capturei" capture="camera" style="visibility:hidden;">
      📂 Browse gallery! <input type="file" accept="image/*" id="capturef" style="visibility:hidden;">
      🎥 Capture video! <input type="file" accept="video/*" capture="camcorder" id="capturev" style="visibility:hidden;">

        What is this?

Share to IRC is a simple web app for sharing media directly from your device to an IRC channel.

Click Tools -> Add to homescreen to create a convenient shortcut for easy and fast access to sharing

  ]]
  if req.method == 'POST'
    fn = req.headers['X-Filename']
    sender = req.headers['X-Sender'] or ''
    text = req.headers['X-Text'] or ''
    file = req.filename
    ivar2\Log 'info', "imageupload: Recieved file name: <#{fn}> datalen: #{#file}, sender: <#{sender}> text: <#{text}>, channel: <#{unescaped_channel}>"
    if fn and file
      html = 'Ok'
      realfn = "#{os.time!}-#{safe fn}"
      save = ->
        --fd = nixio.open "cache/images/#{realfn}", 'w+', 0400
        --written = fd\write file
        --fd\sync!
        --fd\close!
        -- Move tempfile to real location
        fs.move req.filename, "cache/images/#{realfn}"
        if sender ~= ''
          sender = "<#{sender}> "
        msg = "[IRCSNAP] #{sender}#{text\sub(1, 100)} http://irc.lart.no:#{ivar2.config.webserverport}#{urlbase}file/#{realfn}"
        ivar2\Privmsg unescaped_channel, msg
      ok, err = pcall(save)
      unless ok
        ivar2\Log 'error', "imageupload: Error during saving upload: %s", err
    else
      html = 'Not OK'

  send html

-- Attempt to create the cache folder.
nixio.fs.mkdir('cache/images')

PRIVMSG:
  '^%pircsnap$': (source, destination) =>
    channel = urlEncode destination
    say "http://irc.lart.no:#{ivar2.config.webserverport}#{urlbase}?channel=#{channel}"

{:simplehttp, :json, :urlEncode} = require'util'
html2unicode = require 'html'
nixio = require'nixio'
fs = require'nixio.fs'
ltn12 = require'ltn12'

urlbase = '/image/'

safe = (fn) ->
  f, ext = fn\match'^(.*)%.(.-)$'
  f = f\gsub '[^%w%-]', ''
  return f..'.'..ext

ivar2.webserver.regUrl "#{urlbase}(.*)$", (req, res) ->
  send = (body, code, content_type) ->
    if not code then code = 200
    if not content_type then content_type = 'text/html'
    res\set_status code
    res\set_header 'Content-Type', content_type
    res\set_header 'Content-Length', #body
    res\set_body body
    res\send!
    res.on_error = (res, req, err) ->
      ivar2\Log 'error', 'imageupload: error: %s', err

  file = req.url\match '/file/(.*)$'
  if file
    fn = "cache/images/#{safe file}"
    size = nixio.fs.stat(fn).size
    fd = ltn12.source.file(io.open fn, 'rb')
    --body = fd\read '*a'
    --fd\close!
    content_type = 'image/jpeg'
    if file\match '.png'
      content_type = 'image/png'
    if file\match '.svg'
      content_type = 'image/svg'
    if file\match '.mp4'
      content_type = 'video/mp4'
    if file\match '.mp3'
      content_type = 'audio/mp3'

    res\set_status code
    res\set_header 'Content-Type', content_type
    res\set_header 'Content-Length', size
    res\set_body fd
    res\send!
    ivar2\Log 'error', 'imageupload: serving: %s', fn

    return

  channel = req.url\match('channel=(.+)')
  unless channel
    send 'Invalid channel', 404
    return

  channel = html2unicode channel
  unescaped_channel = channel\gsub '%%23', '#'

  html = [[
  <!DOCTYPE html>

  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
  <meta name="mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="apple-mobile-web-app-title" content="IRCSNAP ]]..unescaped_channel..[[">
  <meta name="theme-color" content="#8f4099">

  <title>IRCSNAP</title>

Share to IRC app - ]]..unescaped_channel..[[ edition

Fill in your nick/name and optional text and then click on one of the two yellow buttons to attach image. It will appear instantly on IRC!</p
    <form>

        <input id="sender" type="text" required>

        Your name

        <input id="text" type="text" required>

        Attach text

    </form>

Uploading. Percent complete: 0 %

      📷 Snap picture! <input type="file" accept="image/*" id="capturei" capture="camera" style="visibility:hidden;">
      📂 Browse gallery! <input type="file" accept="image/*" id="capturef" style="visibility:hidden;">
      🎥 Capture video! <input type="file" accept="video/*" capture="camcorder" id="capturev" style="visibility:hidden;">

        What is this?

Share to IRC is a simple web app for sharing media directly from your device to an IRC channel.

Click Tools -> Add to homescreen to create a convenient shortcut for easy and fast access to sharing

  ]]
  if req.method == 'POST'
    fn = req.headers['X-Filename']
    sender = req.headers['X-Sender'] or ''
    text = req.headers['X-Text'] or ''
    file = req.filename
    ivar2\Log 'info', "imageupload: Recieved file name: <#{fn}> datalen: #{#file}, sender: <#{sender}> text: <#{text}>, channel: <#{unescaped_channel}>"
    if fn and file
      html = 'Ok'
      realfn = "#{os.time!}-#{safe fn}"
      save = ->
        --fd = nixio.open "cache/images/#{realfn}", 'w+', 0400
        --written = fd\write file
        --fd\sync!
        --fd\close!
        -- Move tempfile to real location
        fs.move req.filename, "cache/images/#{realfn}"
        if sender ~= ''
          sender = "<#{sender}> "
        msg = "[IRCSNAP] #{sender}#{text\sub(1, 100)} http://irc.lart.no:#{ivar2.config.webserverport}#{urlbase}file/#{realfn}"
        ivar2\Privmsg unescaped_channel, msg
      ok, err = pcall(save)
      unless ok
        ivar2\Log 'error', "imageupload: Error during saving upload: %s", err
    else
      html = 'Not OK'

  send html

-- Attempt to create the cache folder.
nixio.fs.mkdir('cache/images')

PRIVMSG:
  '^%pircsnap$': (source, destination) =>
    channel = urlEncode destination
    say "http://irc.lart.no:#{ivar2.config.webserverport}#{urlbase}?channel=#{channel}"


Remove debug output from layout

Remove debug output from layout

html = require "lapis.html"
import to_json from require "lapis.util"

class DefaultLayout extends html.Widget
    content: =>
        js = (require 'views.javascript')!.js
        html_5 ->
            head ->
                meta charset: "UTF-8"

                -- This enables web-scale mode, right?
                -- meta name: "viewport", content:"initial-scale=1"

                link href: "/static/style.css", rel: "stylesheet", type: "text/css"
                link rel: "shortcut icon", href: "/favicon.ico?v=2"
                title (@m and @m.title) or "MG CS Week"

            body id: @page_id, ->
                @content_for "header"

                div id: "skrollr-body"
                main id: "content", ->
                    @content_for "inner"

                @content_for "footer"
                for scrpt in *js
                    with scrpt
                        contents = .contents
                        .contents = nil
                        script scrpt, ->
                            raw contents

    upload: =>

html = require "lapis.html"
import to_json from require "lapis.util"

class DefaultLayout extends html.Widget
    content: =>
        js = (require 'views.javascript')!.js
        print to_json(js)
        html_5 ->
            head ->
                meta charset: "UTF-8"

                -- This enables web-scale mode, right?
                -- meta name: "viewport", content:"initial-scale=1"

                link href: "/static/style.css", rel: "stylesheet", type: "text/css"
                link rel: "shortcut icon", href: "/favicon.ico?v=2"
                title (@m and @m.title) or "MG CS Week"

            body id: @page_id, ->
                @content_for "header"

                div id: "skrollr-body"
                main id: "content", ->
                    @content_for "inner"

                @content_for "footer"
                print to_json(js)
                for scrpt in *js
                    with scrpt
                        contents = .contents
                        .contents = nil
                        script scrpt, ->
                            raw contents

    upload: =>


redditjoke: add meanjokes

redditjoke: add meanjokes

util = require'util'
json = util.json
simplehttp = util.simplehttp

pick = (data) ->
  children = data.data.children
  thejoke = children[math.random 1, #children]
  title = thejoke.data.title
  joketext = title .. ' ' .. thejoke.data.selftext
  return joketext

joke = (subreddit, cb) ->
  simplehttp "http://www.reddit.com/r/#{subreddit}.json", (data) ->
    data = json.decode data
    -- try to find a short joke
    for i=0, 100 do
      joketext = pick data
      if #joketext < 300
        return cb joketext

PRIVMSG:
  '^%pjoke$': (source, destination, arg) =>
    joke 'jokes', (text) ->
      say text
  '^%pdadjoke$': (source, destination, arg) =>
    joke 'dadjokes', (text) ->
      say text
  '^%pmeanjoke$': (source, destination, arg) =>
    joke 'meanjokes', (text) ->
      say text

util = require'util'
json = util.json
simplehttp = util.simplehttp

pick = (data) ->
  children = data.data.children
  thejoke = children[math.random 1, #children]
  title = thejoke.data.title
  joketext = title .. ' ' .. thejoke.data.selftext
  return joketext

joke = (subreddit, cb) ->
  simplehttp "http://www.reddit.com/r/#{subreddit}.json", (data) ->
    data = json.decode data
    -- try to find a short joke
    for i=0, 100 do
      joketext = pick data
      if #joketext < 300
        return cb joketext

PRIVMSG:
  '^%pjoke': (source, destination, arg) =>
    joke 'jokes', (text) ->
      say text
  '^%pdadjoke': (source, destination, arg) =>
    joke 'dadjokes', (text) ->
      say text


Update everything.moon with new files

Update everything.moon with new files

-- Auto-generated with ./create_everything.sh on 2014-01-21 02:13:06

export ^

-- We tell Moonscript to export classes globally. Thus, we prevent collisions
-- based on namespace since we don't know what is going to be used.
--
-- This provides two options for getting the class, calling require again and
-- assigning it to a local variable. Alternatively, the namespaced version can
-- be used.

LovePunk = {}
LovePunk.debug = {}
LovePunk.geometry = {}
LovePunk.graphics = {}
LovePunk.graphics.atlas = {}
LovePunk.graphics.prototype = {}
LovePunk.masks = {}
LovePunk.tweens = {}
LovePunk.tweens.misc = {}
LovePunk.tweens.motion = {}
LovePunk.tweens.sound = {}
LovePunk.utils = {}

-- Now load each file to the correctly namespaced table
LovePunk.debug.Console = require "LovePunk.debug.Console"
LovePunk.debug.LayerList = require "LovePunk.debug.LayerList"
LovePunk.Engine = require "LovePunk.Engine"
LovePunk.Entity = require "LovePunk.Entity"
LovePunk.geometry.Matrix = require "LovePunk.geometry.Matrix"
LovePunk.geometry.Point = require "LovePunk.geometry.Point"
LovePunk.geometry.Rectangle = require "LovePunk.geometry.Rectangle"
LovePunk.Graphic = require "LovePunk.Graphic"
LovePunk.graphics.Animation = require "LovePunk.graphics.Animation"
LovePunk.graphics.atlas.AtlasData = require "LovePunk.graphics.atlas.AtlasData"
LovePunk.graphics.atlas.AtlasRegion = require "LovePunk.graphics.atlas.AtlasRegion"
LovePunk.graphics.atlas.Atlas = require "LovePunk.graphics.atlas.Atlas"
LovePunk.graphics.atlas.TextureAtlas = require "LovePunk.graphics.atlas.TextureAtlas"
LovePunk.graphics.atlas.TileAtlas = require "LovePunk.graphics.atlas.TileAtlas"
LovePunk.graphics.Backdrop = require "LovePunk.graphics.Backdrop"
LovePunk.graphics.Canvas = require "LovePunk.graphics.Canvas"
LovePunk.graphics.Emitter = require "LovePunk.graphics.Emitter"
LovePunk.graphics.Graphiclist = require "LovePunk.graphics.Graphiclist"
LovePunk.graphics.Image = require "LovePunk.graphics.Image"
LovePunk.graphics.Particle = require "LovePunk.graphics.Particle"
LovePunk.graphics.ParticleType = require "LovePunk.graphics.ParticleType"
LovePunk.graphics.PreRotation = require "LovePunk.graphics.PreRotation"
LovePunk.graphics.prototype.Circle = require "LovePunk.graphics.prototype.Circle"
LovePunk.graphics.prototype.Rect = require "LovePunk.graphics.prototype.Rect"
LovePunk.graphics.Spritemap = require "LovePunk.graphics.Spritemap"
LovePunk.graphics.Stamp = require "LovePunk.graphics.Stamp"
LovePunk.graphics.Text = require "LovePunk.graphics.Text"
LovePunk.graphics.TiledImage = require "LovePunk.graphics.TiledImage"
LovePunk.graphics.TiledSpritemap = require "LovePunk.graphics.TiledSpritemap"
LovePunk.graphics.Tilemap = require "LovePunk.graphics.Tilemap"
LovePunk.LP = require "LovePunk.LP"
LovePunk.Mask = require "LovePunk.Mask"
LovePunk.masks.Circle = require "LovePunk.masks.Circle"
LovePunk.masks.Grid = require "LovePunk.masks.Grid"
LovePunk.masks.Hitbox = require "LovePunk.masks.Hitbox"
LovePunk.masks.Imagemask = require "LovePunk.masks.Imagemask"
LovePunk.masks.Masklist = require "LovePunk.masks.Masklist"
LovePunk.masks.Pixelmask = require "LovePunk.masks.Pixelmask"
LovePunk.masks.Polygon = require "LovePunk.masks.Polygon"
LovePunk.masks.SlopedGrid = require "LovePunk.masks.SlopedGrid"
LovePunk.RenderMode = require "LovePunk.RenderMode"
LovePunk.Scene = require "LovePunk.Scene"
LovePunk.Screen = require "LovePunk.Screen"
LovePunk.Sfx = require "LovePunk.Sfx"
LovePunk.Tweener = require "LovePunk.Tweener"
LovePunk.Tween = require "LovePunk.Tween"
LovePunk.tweens.misc.Alarm = require "LovePunk.tweens.misc.Alarm"
LovePunk.tweens.misc.AngleTween = require "LovePunk.tweens.misc.AngleTween"
LovePunk.tweens.misc.ColorTween = require "LovePunk.tweens.misc.ColorTween"
LovePunk.tweens.misc.MultiVarTween = require "LovePunk.tweens.misc.MultiVarTween"
LovePunk.tweens.misc.NumTween = require "LovePunk.tweens.misc.NumTween"
LovePunk.tweens.misc.VarTween = require "LovePunk.tweens.misc.VarTween"
LovePunk.tweens.motion.CircularMotion = require "LovePunk.tweens.motion.CircularMotion"
LovePunk.tweens.motion.CubicMotion = require "LovePunk.tweens.motion.CubicMotion"
LovePunk.tweens.motion.LinearMotion = require "LovePunk.tweens.motion.LinearMotion"
LovePunk.tweens.motion.LinearPath = require "LovePunk.tweens.motion.LinearPath"
LovePunk.tweens.motion.Motion = require "LovePunk.tweens.motion.Motion"
LovePunk.tweens.motion.QuadMotion = require "LovePunk.tweens.motion.QuadMotion"
LovePunk.tweens.motion.QuadPath = require "LovePunk.tweens.motion.QuadPath"
LovePunk.tweens.sound.Fader = require "LovePunk.tweens.sound.Fader"
LovePunk.tweens.sound.SfxFader = require "LovePunk.tweens.sound.SfxFader"
LovePunk.tweens.TweenEvent = require "LovePunk.tweens.TweenEvent"
LovePunk.utils.Data = require "LovePunk.utils.Data"
LovePunk.utils.Draw = require "LovePunk.utils.Draw"
LovePunk.utils.Ease = require "LovePunk.utils.Ease"
LovePunk.utils.Input = require "LovePunk.utils.Input"
LovePunk.utils.Joystick = require "LovePunk.utils.Joystick"
LovePunk.utils.Key = require "LovePunk.utils.Key"
LovePunk.utils.moonscript = require "LovePunk.utils.moonscript"
LovePunk.utils.Touch = require "LovePunk.utils.Touch"
LovePunk.World = require "LovePunk.World"

-- Auto-generated with ./create_everything.sh on 2014-01-21 00:04:33

export ^

-- We tell Moonscript to export classes globally. Thus, we prevent collisions
-- based on namespace since we don't know what is going to be used.
--
-- This provides two options for getting the class, calling require again and
-- assigning it to a local variable. Alternatively, the namespaced version can
-- be used.

LovePunk = {}
LovePunk.debug = {}
LovePunk.geometry = {}
LovePunk.graphics = {}
LovePunk.graphics.atlas = {}
LovePunk.graphics.prototype = {}
LovePunk.masks = {}
LovePunk.tweens = {}
LovePunk.tweens.misc = {}
LovePunk.tweens.motion = {}
LovePunk.tweens.sound = {}
LovePunk.utils = {}

-- Now load each file to the correctly namespaced table
LovePunk.debug.Console = require "LovePunk.debug.Console"
LovePunk.debug.LayerList = require "LovePunk.debug.LayerList"
LovePunk.Engine = require "LovePunk.Engine"
LovePunk.Entity = require "LovePunk.Entity"
LovePunk.geometry.Rectangle = require "LovePunk.geometry.Rectangle"
LovePunk.Graphic = require "LovePunk.Graphic"
LovePunk.graphics.Animation = require "LovePunk.graphics.Animation"
LovePunk.graphics.atlas.AtlasData = require "LovePunk.graphics.atlas.AtlasData"
LovePunk.graphics.atlas.AtlasRegion = require "LovePunk.graphics.atlas.AtlasRegion"
LovePunk.graphics.atlas.Atlas = require "LovePunk.graphics.atlas.Atlas"
LovePunk.graphics.atlas.TextureAtlas = require "LovePunk.graphics.atlas.TextureAtlas"
LovePunk.graphics.atlas.TileAtlas = require "LovePunk.graphics.atlas.TileAtlas"
LovePunk.graphics.Backdrop = require "LovePunk.graphics.Backdrop"
LovePunk.graphics.Canvas = require "LovePunk.graphics.Canvas"
LovePunk.graphics.Emitter = require "LovePunk.graphics.Emitter"
LovePunk.graphics.Graphiclist = require "LovePunk.graphics.Graphiclist"
LovePunk.graphics.Image = require "LovePunk.graphics.Image"
LovePunk.graphics.Particle = require "LovePunk.graphics.Particle"
LovePunk.graphics.ParticleType = require "LovePunk.graphics.ParticleType"
LovePunk.graphics.PreRotation = require "LovePunk.graphics.PreRotation"
LovePunk.graphics.prototype.Circle = require "LovePunk.graphics.prototype.Circle"
LovePunk.graphics.prototype.Rect = require "LovePunk.graphics.prototype.Rect"
LovePunk.graphics.Spritemap = require "LovePunk.graphics.Spritemap"
LovePunk.graphics.Stamp = require "LovePunk.graphics.Stamp"
LovePunk.graphics.Text = require "LovePunk.graphics.Text"
LovePunk.graphics.TiledImage = require "LovePunk.graphics.TiledImage"
LovePunk.graphics.TiledSpritemap = require "LovePunk.graphics.TiledSpritemap"
LovePunk.graphics.Tilemap = require "LovePunk.graphics.Tilemap"
LovePunk.LP = require "LovePunk.LP"
LovePunk.Mask = require "LovePunk.Mask"
LovePunk.masks.Circle = require "LovePunk.masks.Circle"
LovePunk.masks.Grid = require "LovePunk.masks.Grid"
LovePunk.masks.Hitbox = require "LovePunk.masks.Hitbox"
LovePunk.masks.Imagemask = require "LovePunk.masks.Imagemask"
LovePunk.masks.Masklist = require "LovePunk.masks.Masklist"
LovePunk.masks.Pixelmask = require "LovePunk.masks.Pixelmask"
LovePunk.masks.Polygon = require "LovePunk.masks.Polygon"
LovePunk.masks.SlopedGrid = require "LovePunk.masks.SlopedGrid"
LovePunk.RenderMode = require "LovePunk.RenderMode"
LovePunk.Scene = require "LovePunk.Scene"
LovePunk.Screen = require "LovePunk.Screen"
LovePunk.Sfx = require "LovePunk.Sfx"
LovePunk.Tweener = require "LovePunk.Tweener"
LovePunk.Tween = require "LovePunk.Tween"
LovePunk.tweens.misc.Alarm = require "LovePunk.tweens.misc.Alarm"
LovePunk.tweens.misc.AngleTween = require "LovePunk.tweens.misc.AngleTween"
LovePunk.tweens.misc.ColorTween = require "LovePunk.tweens.misc.ColorTween"
LovePunk.tweens.misc.MultiVarTween = require "LovePunk.tweens.misc.MultiVarTween"
LovePunk.tweens.misc.NumTween = require "LovePunk.tweens.misc.NumTween"
LovePunk.tweens.misc.VarTween = require "LovePunk.tweens.misc.VarTween"
LovePunk.tweens.motion.CircularMotion = require "LovePunk.tweens.motion.CircularMotion"
LovePunk.tweens.motion.CubicMotion = require "LovePunk.tweens.motion.CubicMotion"
LovePunk.tweens.motion.LinearMotion = require "LovePunk.tweens.motion.LinearMotion"
LovePunk.tweens.motion.LinearPath = require "LovePunk.tweens.motion.LinearPath"
LovePunk.tweens.motion.Motion = require "LovePunk.tweens.motion.Motion"
LovePunk.tweens.motion.QuadMotion = require "LovePunk.tweens.motion.QuadMotion"
LovePunk.tweens.motion.QuadPath = require "LovePunk.tweens.motion.QuadPath"
LovePunk.tweens.sound.Fader = require "LovePunk.tweens.sound.Fader"
LovePunk.tweens.sound.SfxFader = require "LovePunk.tweens.sound.SfxFader"
LovePunk.tweens.TweenEvent = require "LovePunk.tweens.TweenEvent"
LovePunk.utils.Data = require "LovePunk.utils.Data"
LovePunk.utils.Draw = require "LovePunk.utils.Draw"
LovePunk.utils.Ease = require "LovePunk.utils.Ease"
LovePunk.utils.Input = require "LovePunk.utils.Input"
LovePunk.utils.Joystick = require "LovePunk.utils.Joystick"
LovePunk.utils.Key = require "LovePunk.utils.Key"
LovePunk.utils.moonscript = require "LovePunk.utils.moonscript"
LovePunk.utils.Touch = require "LovePunk.utils.Touch"
LovePunk.World = require "LovePunk.World"


Update users.moon

Update users.moon

import Model from require "lapis.db.model"

class Users extends Model
  @table_name: => "lazuli_module_user_management_users"

import Model, enum from require "lapis.db.model"

class Users extends Model
  @table_name: => "users"


try to avoid tbezier errors

try to avoid tbezier errors

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

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

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

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

--
-- 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
-- 25   Male
-- 26   Hoof_Fluffers
-- 27   o3o
-- 28   Ear_Fluffers
-- 29   Fangs
-- 30   Claw_Teeth
-- 31   Fang_Test
-- 32   angry_eyes
-- 33   sad_eyes
-- 34   Eyes_Blink_Lower
-- 35   Male_2
-- 36   Buff_Body
-- 37   Manliest_Chin
-- 38   Lowerlid_Raise
-- 39   Happy_Eyes
-- 40   Duck

BLINK_CURVE = {0, 0.2, 0.45, 0.65, 0.9, 1, 1, 0.75, 0.55, 0.25, 0.15, 0}
BREATH_CURVE = {0, 0, 0, 0.1, 0.3, 0.55, 0.85, 1, 1, 0.85, 0.65, 0.4, 0.2, 0, 0, 0}

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

class FlexState extends PPM2.ModifierBase
	@SetupModifiers: =>
		@RegisterModifier('Speed', 0)
		@RegisterModifier('Scale', 0)
		@RegisterModifier('Weight', 0)

	new: (controller, flexName = '', flexID = 0, scale = 1, speed = 1, active = true, min = 0, max = 1, useModifiers = true) =>
		super()
		@controller = controller
		@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
		@modifiers = {}
		@useModifiers = useModifiers
		@active = active
		@useLerp = true
		@lerpMultiplier = 1
		@activeID = "DisableFlex#{@flexName}"

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

	GetFlexID: => @flexID
	GetFlexName: => @flexName
	SetUseLerp: (val = true) => @useLerp = val
	GetUseLerp: => @useLerp
	UseLerp: => @useLerp
	SetLerpModify: (val = 1) => @lerpMultiplier = val
	GetLerpModify: => @lerpMultiplier
	LerpModify: => @lerpMultiplier

	GetEntity: => @controller\GetEntity()
	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

	GetIsActive: => @active
	SetIsActive: (val = true) => @active = val

	AddValue: (val = 0) => @SetValue(@current + val)
	AddRealValue: (val = 0) => @SetRealValue(@target + val)
	Think: (ent = @GetEntity(), delta = 0) =>
		return if not @active

		if @useModifiers
			@current = 0
			@scale = @originalscale * @scaleModify
			@speed = @originalspeed * @speedModify

			if @useLerp
				for i = 1, #@WeightModifiers
					@modifiers[i] = Lerp(delta * 15 * @speed * @speedModify * @lerpMultiplier, @modifiers[i] or 0, @WeightModifiers[i])
					@current += @modifiers[i]
			else
				@current += @WeightModifiers[i] for i = 1, #@WeightModifiers

			@scale += modif for _, modif in ipairs @ScaleModifiers
			@speed += modif for _, modif in ipairs @SpeedModifiers
			@current = math.Clamp(@current, @min, @max) * @scale

		ent\SetFlexWeight(@flexID, @current)
	DataChanges: (state) =>
		return if state\GetKey() ~= @activeID
		@SetIsActive(not state\GetValue())
		@GetController()\RebuildIterableList()
		@Reset()
	Reset: (resetVars = true) =>
		for name, id in pairs @modifiersNames
			@ResetModifiers(name)
		if resetVars
			@scaleModify = 1
			@speedModify = 1
		@scale = @originalscale * @scaleModify
		@speed = @originalspeed * @speedModify
		@target = 0
		@current = 0
		@GetEntity()\SetFlexWeight(@flexID, 0) if IsValid(@GetEntity())

PPM2.FlexState = FlexState

class FlexSequence extends PPM2.SequenceBase
	new: (controller, data) =>
		super(controller, data)

		{
			'ids': @flexIDsIterable
			'numid': @numid
		} = data

		@flexIDS = {}
		@flexStates = {}
		i = 1
		for _, id in ipairs 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

		@controller = controller
		@Launch()

	GetController: => @controller
	GetModifierID: (id = '') => @flexIDS[id]
	GetFlexState: (id = '') => @flexStates[id]

	Think: (delta = 0) =>
		return false if not IsValid(@GetEntity())
		super(delta)

	Stop: =>
		super()
		return unless @parent
		@parent\GetFlexState(id)\ResetModifiers(@name) for _, id in ipairs @flexIDsIterable

	SetModifierWeight: (id = '', val = 0) => @GetFlexState(id)\SetModifierWeight(@GetModifierID(id), val)
	SetModifierSpeed: (id = '', val = 0) => @GetFlexState(id)\SetModifierSpeed(@GetModifierID(id), val)
	SetUseLerp: (id = '', status = true) => @GetFlexState(id)\SetUseLerp(status)
	GetUseLerp: (id = '') => @GetFlexState(id)\GetUseLerp()

PPM2.FlexSequence = FlexSequence

class PonyFlexController extends PPM2.ControllerChildren
	@AVALIABLE_CONTROLLERS = {}
	@MODELS = {'models/ppm/player_default_base_new.mdl', 'models/ppm/player_default_base_new_nj.mdl'}

	@FLEX_LIST = {
		{flex: 'eyes_updown',       scale: 1, speed: 1, active: false}
		{flex: 'eyes_rightleft',    scale: 1, speed: 1, active: false}
		{flex: 'JawOpen',           scale: 1, speed: 1, active: true}
		{flex: 'JawClose',          scale: 1, speed: 1, active: true}
		{flex: 'Smirk',             scale: 1, speed: 1, active: true}
		{flex: 'Frown',             scale: 1, speed: 1, active: true}
		{flex: 'Stretch',           scale: 1, speed: 1, active: false}
		{flex: 'Pucker',            scale: 1, speed: 1, active: false}
		{flex: 'Grin',              scale: 1, speed: 1, active: true}
		{flex: 'CatFace',           scale: 1, speed: 1, active: true}
		{flex: 'Mouth_O',           scale: 1, speed: 1, active: true}
		{flex: 'Mouth_O2',          scale: 1, speed: 1, active: true}
		{flex: 'Mouth_Full',        scale: 1, speed: 1, active: false}
		{flex: 'Tongue_Out',        scale: 1, speed: 1, active: true}
		{flex: 'Tongue_Up',         scale: 1, speed: 1, active: true}
		{flex: 'Tongue_Down',       scale: 1, speed: 1, active: true}
		{flex: 'NoEyelashes',       scale: 1, speed: 1, active: false}
		{flex: 'Eyes_Blink',        scale: 1, speed: 1, active: false}
		{flex: 'Left_Blink',        scale: 1, speed: 1, active: true}
		{flex: 'Right_Blink',       scale: 1, speed: 1, active: true}
		{flex: 'Scrunch',           scale: 1, speed: 1, active: true}
		{flex: 'FatButt',           scale: 1, speed: 1, active: false}
		{flex: 'Stomach_Out',       scale: 1, speed: 1, active: true}
		{flex: 'Stomach_In',        scale: 1, speed: 1, active: true}
		{flex: 'Throat_Bulge',      scale: 1, speed: 1, active: true}
		{flex: 'Male',              scale: 1, speed: 1, active: false}
		{flex: 'Hoof_Fluffers',     scale: 1, speed: 1, active: false}
		{flex: 'o3o',               scale: 1, speed: 1, active: true}
		{flex: 'Ear_Fluffers',      scale: 1, speed: 1, active: false}
		{flex: 'Fangs',             scale: 1, speed: 1, active: false}
		{flex: 'Claw_Teeth',        scale: 1, speed: 1, active: false}
		{flex: 'Fang_Test',         scale: 1, speed: 1, active: false}
		{flex: 'angry_eyes',        scale: 1, speed: 1, active: true}
		{flex: 'sad_eyes',          scale: 1, speed: 1, active: true}
		{flex: 'Eyes_Blink_Lower',  scale: 1, speed: 1, active: true}
		{flex: 'Male_2',            scale: 1, speed: 1, active: false}
		{flex: 'Buff_Body',         scale: 1, speed: 1, active: false}
		{flex: 'Manliest_Chin',     scale: 1, speed: 1, active: false}
		{flex: 'Lowerlid_Raise',    scale: 1, speed: 1, active: false}
		{flex: 'Happy_Eyes',        scale: 1, speed: 1, active: true}
		{flex: 'Duck',              scale: 1, speed: 1, active: true}

	}

	@SEQUENCES = {
		{
			'name': 'anger'
			'autostart': false
			'repeat': false
			'time': 5
			'ids': {'Frown', 'Grin', 'angry_eyes', 'Scrunch'}
			'reset': =>
				@SetTime(math.random(15, 45) / 10)
				@lastStrengthUpdate = @lastStrengthUpdate or 0
				if @lastStrengthUpdate < CurTimeL()
					@lastStrengthUpdate = CurTimeL() + 2
					@frownStrength = math.random(40, 100) / 100
					@grinStrength = math.random(15, 40) / 100
					@angryStrength = math.random(30, 80) / 100
					@scrunchStrength = math.random(50, 100) / 100
					@SetModifierWeight(1, @frownStrength)
					@SetModifierWeight(2, @grinStrength)
					@SetModifierWeight(3, @angryStrength)
					@SetModifierWeight(4, @scrunchStrength)
		}

		{
			'name': 'sad'
			'autostart': false
			'repeat': false
			'time': 5
			'ids': {'Frown', 'Grin', 'sad_eyes'}
			'reset': =>
				@SetTime(math.random(15, 45) / 10)
				@lastStrengthUpdate = @lastStrengthUpdate or 0
				if @lastStrengthUpdate < CurTimeL()
					@lastStrengthUpdate = CurTimeL() + 2
					@frownStrength = math.random(40, 100) / 100
					@grinStrength = math.random(15, 40) / 100
					@angryStrength = math.random(30, 80) / 100
			'func': (delta, timeOfAnim) =>
				@SetModifierWeight(1, @frownStrength)
				@SetModifierWeight(2, @grinStrength)
				@SetModifierWeight(3, @angryStrength)
		}

		{
			'name': 'ugh'
			'autostart': false
			'repeat': false
			'time': 5
			'ids': {'sad_eyes', 'Eyes_Blink_Lower'}
			'reset': =>
				@SetModifierWeight(1, math.Rand(0.27, 0.34))
				@SetModifierWeight(2, math.Rand(0.3, 0.35))
				@PauseSequence('eyes_blink')
				@PauseSequence('eyes_idle')
		}

		{
			'name': 'suggestive_eyes'
			'autostart': false
			'repeat': false
			'time': 5
			'ids': {'sad_eyes', 'Eyes_Blink_Lower'}
			'reset': =>
				@SetModifierWeight(1, 0.28)
				@SetModifierWeight(2, 0.4)
				@PauseSequence('eyes_blink')
				@PauseSequence('eyes_idle')
		}

		{
			'name': 'lips_lick'
			'autostart': false
			'repeat': false
			'time': 5
			'ids': {'Tongue_Out', 'Tongue_Up'}
			'reset': =>
				@SetModifierWeight(1, 0.9)
			'func': (delta, timeOfAnim) =>
				@SetModifierWeight(2, 0.75 + math.sin(CurTimeL() * 7) * 0.25)
		}

		{
			'name': 'tongue_pullout'
			'autostart': false
			'repeat': false
			'time': 5
			'ids': {'Tongue_Out'}
			'func': (delta, timeOfAnim) =>
				@SetModifierWeight(1, 0.15 + math.sin(CurTimeL() * 10) * 0.1)
		}

		{
			'name': 'tongue_pullout_twitch'
			'autostart': false
			'repeat': false
			'time': 5
			'ids': {'Tongue_Out'}
			'func': (delta, timeOfAnim) =>
				@SetModifierWeight(1, 0.5 + math.sin(CurTimeL() * 4) * 0.5)
		}

		{
			'name': 'tongue_pullout_twitch_fast'
			'autostart': false
			'repeat': false
			'time': 5
			'ids': {'Tongue_Out'}
			'func': (delta, timeOfAnim) =>
				@SetModifierWeight(1, 0.5 + math.sin(CurTimeL() * 8) * 0.5)
		}

		{
			'name': 'suggestive_open'
			'autostart': false
			'repeat': false
			'time': 5
			'ids': {'Pucker', 'JawOpen', 'Scrunch'}
			'reset': =>
				@SetModifierWeight(1, math.Rand(0.28, 0.34))
				@SetModifierWeight(2, math.Rand(0.35, 0.40))
				@SetModifierWeight(3, math.Rand(0.45, 0.50))
		}

		{
			'name': 'suggestive_open_anim'
			'autostart': false
			'repeat': false
			'time': 5
			'ids': {'Pucker', 'JawOpen', 'Scrunch'}
			'reset': =>
				@SetModifierWeight(1, math.Rand(0.28, 0.34))
				@SetModifierWeight(3, math.Rand(0.45, 0.50))
			'func': (delta, timeOfAnim) =>
				@SetModifierWeight(2, 0.2 + math.sin(CurTimeL() * 16) * 0.07)
		}

		{
			'name': 'face_smirk'
			'autostart': false
			'repeat': false
			'time': 5
			'ids': {'Smirk', 'Frown'}
			'reset': =>
				@SetModifierWeight(1, 0.78)
				@SetModifierWeight(2, 0.61)
		}

		{
			'name': 'eyes_idle'
			'autostart': true
			'repeat': true
			'time': 5
			'ids': {'Left_Blink', 'Right_Blink'}
			'func': (delta, timeOfAnim) =>
				return false if @GetEntity()\GetNWBool('PPM2.IsDeathRagdoll')
				value = math.abs(math.sin(CurTimeL() * .5) * .15)
				@SetModifierWeight(1, value)
				@SetModifierWeight(2, value)
		}

		{
			'name': 'eyes_close'
			'autostart': true
			'repeat': true
			'time': 5
			'ids': {'Left_Blink', 'Right_Blink', 'Frown'}
			'func': (delta, timeOfAnim) =>
				return if not @GetEntity()\GetNWBool('PPM2.IsDeathRagdoll')
				@SetModifierWeight(1, 1)
				@SetModifierWeight(2, 1)
				@SetModifierWeight(3, 0.5)
		}

		{
			'name': 'body_idle'
			'autostart': true
			'repeat': true
			'time': 2
			'ids': {'Stomach_Out'}
			'func': (delta, timeOfAnim) =>
				return false if @GetEntity()\GetNWBool('PPM2.IsDeathRagdoll')
				return if timeOfAnim < 0 or timeOfAnim > 1
				@SetModifierWeight(1, math.tbezier(timeOfAnim, BREATH_CURVE) * 0.35)
		}

		{
			'name': 'health_idle'
			'autostart': true
			'repeat': true
			'time': 5
			'ids': {'Frown', 'Left_Blink', 'Right_Blink', 'Scrunch', 'Mouth_O', 'JawOpen', 'Grin'}
			'func': (delta, timeOfAnim) =>
				return false if not @GetEntity()\IsPlayer() and not @GetEntity()\IsNPC() and @GetEntity().Type ~= 'nextbot'
				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 = @GetEntity()\Health(), @GetEntity()\GetMaxHealth()
				mhp = 1 if mhp == 0
				div = hp / mhp
				strength = math.Clamp(1.5 - div * 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(CurTimeL() * strength * 3) * .1)
				else
					JawOpenState\SetModifierWeight(JawOpen, 0)

				if div >= 2
					@SetModifierWeight(7, .5)
				else
					@SetModifierWeight(7, 0)
		}

		{
			'name': 'greeny'
			'autostart': false
			'repeat': false
			'time': 2
			'ids': {'Grin'}
			'func': (delta, timeOfAnim) =>
				Grin = @GetModifierID(1)
				GrinState = @GetFlexState(1)
				strength = .5 + math.sin(CurTimeL() * 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': 'o3o'
			'autostart': false
			'repeat': false
			'time': 3
			'ids': {'o3o'}
			'func': (delta, timeOfAnim) =>
				@SetModifierWeight(1, 1)
		}

		{
			'name': 'owo_alternative'
			'autostart': false
			'repeat': false
			'time': 5
			'ids': {'o3o', 'JawOpen'}
			'reset': (delta, timeOfAnim) =>
				@SetModifierWeight(1, math.Rand(0.8, 1))
				@SetModifierWeight(2, math.Rand(0.05, 0.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': 'angry_tongue'
			'autostart': false
			'repeat': false
			'time': 6
			'ids': {'Frown', 'Grin', 'angry_eyes', 'Scrunch', 'JawOpen', 'Tongue_Out'}
			'reset': (delta, timeOfAnim) =>
				@SetModifierWeight(1, math.random(40, 100) / 100)
				@SetModifierWeight(2, math.random(15, 40) / 100)
				@SetModifierWeight(3, math.random(30, 80) / 100)
				@SetModifierWeight(4, math.random(50, 100) / 100)
				@SetModifierWeight(5, math.random(10, 15) / 100)
				@SetModifierWeight(6, math.random(80, 100) / 100)
		}

		{
			'name': 'pffff'
			'autostart': false
			'repeat': false
			'time': 6
			'ids': {'Frown', 'Grin', 'angry_eyes', 'Scrunch', 'JawOpen', 'Tongue_Out', 'Tongue_Down', 'Tongue_Up'}
			'reset': =>
				@SetModifierWeight(1, math.random(40, 100) / 100)
				@SetModifierWeight(2, math.random(15, 40) / 100)
				@SetModifierWeight(3, math.random(30, 80) / 100)
				@SetModifierWeight(4, math.random(50, 100) / 100)
				@SetModifierWeight(5, math.random(10, 15) / 100)
				@SetModifierWeight(6, math.random(80, 100) / 100)
			'func': (delta, timeOfAnim) =>
				val = math.sin(CurTimeL() * 8) * .6
				if val > 0
					@SetModifierWeight(7, val)
					@SetModifierWeight(8, 0)
				else
					@SetModifierWeight(7, 0)
					@SetModifierWeight(8, -val)
		}

		{
			'name': 'cat'
			'autostart': false
			'repeat': false
			'time': 5
			'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}
				@SetModifierSpeed(1, 2)
				@SetModifierSpeed(2, 2)
				@SetModifierSpeed(3, 2)
				@SetModifierSpeed(4, 2)
			'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}
				@SetModifierSpeed(1, 2)
				@SetModifierSpeed(2, 2)
				@SetModifierSpeed(3, 2)
				@SetModifierSpeed(4, 2)
			'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 = @GetEntity()\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': {'Eyes_Blink_Lower'}
			'create': =>
				@SetUseLerp(1, false)

			'reset': =>
				@nextBlink = math.random(300, 600) / 1000
				@nextBlinkLength = math.random(15, 30) / 800
				@min, @max = @nextBlink, @nextBlink + @nextBlinkLength

			'func': (delta, timeOfAnim) =>
				return false if @GetEntity()\GetNWBool('PPM2.IsDeathRagdoll')
				if @min > timeOfAnim or @max < timeOfAnim
					if @blinkHit
						@blinkHit = false
						@SetModifierWeight(1, 0)

					return

				len = (timeOfAnim - @min) / @nextBlinkLength
				blink = math.tbezier(len\clamp(0, 1), BLINK_CURVE)

				@SetModifierWeight(1, blink)
				-- print(len, blink)

				@blinkHit = true
		}

		{
			'name': 'hurt'
			'autostart': false
			'repeat': false
			'time': 4
			'ids': {'JawOpen', 'Frown', 'Grin', 'Scrunch'}
			'reset': (delta, timeOfAnim) =>
				@SetModifierWeight(1, math.random(4, 16) / 100)
				@SetModifierWeight(2, math.random(60, 70) / 100)
				@SetModifierWeight(3, math.random(30, 40) / 100)
				@SetModifierWeight(4, math.random(70, 90) / 100)
		}

		{
			'name': 'kill_grin'
			'autostart': false
			'repeat': false
			'time': 8
			'ids': {'Smirk', 'Frown', 'Grin'}
			'func': (delta, timeOfAnim) =>
				@SetModifierWeight(1, .51)
				@SetModifierWeight(2, .38)
				@SetModifierWeight(3, .66)
		}

		{
			'name': 'sorry'
			'autostart': false
			'repeat': false
			'time': 8
			'ids': {'Frown', 'Stretch', 'Grin', 'Scrunch', 'sad_eyes'}
			'create': =>
				@SetModifierWeight(1, math.random(45, 75) / 100)
				@SetModifierWeight(2, math.random(45, 75) / 100)
				@SetModifierWeight(3, math.random(70, 100) / 100)
				@SetModifierWeight(4, math.random(7090, 100) / 100)
		}

		{
			'name': 'scrunch'
			'autostart': false
			'repeat': false
			'time': 6
			'ids': {'Scrunch'}
			'create': =>
				@SetModifierWeight(1, math.random(80, 100) / 100)
		}

		{
			'name': 'gulp'
			'autostart': false
			'repeat': false
			'time': 2
			'ids': {'Throat_Bulge', 'Frown', 'Grin'}
			'create': =>
				@SetModifierWeight(2, 1)
				@SetModifierWeight(3, math.random(35, 55) / 100)
			'func': (delta, timeOfAnim) =>
				if timeOfAnim > 0.5
					@SetModifierWeight(1, (1 - timeOfAnim) * 2)
				else
					@SetModifierWeight(1, timeOfAnim * 2)
		}

		{
			'name': 'blahblah'
			'autostart': false
			'repeat': false
			'time': 3
			'ids': {'o3o', 'Mouth_O'}
			'create': =>
				@SetModifierWeight(1, 1)
				@talkAnim = [math.random(50, 70) / 100 for i = 0, 1, 0.05]
			'func': (delta, timeOfAnim) =>
				cPos = math.floor(timeOfAnim * 20) + 1
				data = @talkAnim[cPos]
				return if not data
				@SetModifierWeight(2, data)
		}

		{
			'name': 'wink_left'
			'autostart': false
			'repeat': false
			'time': 2
			'ids': {'Frown', 'Stretch', 'Grin', 'Left_Blink'}
			'create': =>
				@SetModifierWeight(1, math.random(40, 60) / 100)
				@SetModifierWeight(2, math.random(30, 50) / 100)
				@SetModifierWeight(3, math.random(60, 100) / 100)
				@SetModifierWeight(4, 1)
				@PauseSequence('eyes_blink')
		}

		{
			'name': 'wink_right'
			'autostart': false
			'repeat': false
			'time': 2
			'ids': {'Frown', 'Stretch', 'Grin', 'Right_Blink'}
			'create': =>
				@SetModifierWeight(1, math.random(40, 60) / 100)
				@SetModifierWeight(2, math.random(30, 50) / 100)
				@SetModifierWeight(3, math.random(60, 100) / 100)
				@SetModifierWeight(4, 1)
				@PauseSequence('eyes_blink')
		}

		{
			'name': 'happy_eyes'
			'autostart': false
			'repeat': false
			'time': 3
			'ids': {'Happy_Eyes'}
			'create': =>
				@SetModifierWeight(1, 1)
				@PauseSequence('eyes_blink')
		}

		{
			'name': 'happy_grin'
			'autostart': false
			'repeat': false
			'time': 3
			'ids': {'Happy_Eyes', 'Grin'}
			'create': =>
				@SetModifierWeight(1, 1)
				@SetModifierWeight(2, 1)
				@PauseSequence('eyes_blink')
		}

		{
			'name': 'duck'
			'autostart': false
			'repeat': false
			'time': 3
			'ids': {'Duck'}
			'create': =>
				@SetModifierWeight(1, math.random(70, 90) / 100)
		}

		{
			'name': 'duck_insanity'
			'autostart': false
			'repeat': false
			'time': 3
			'ids': {'Duck'}
			'func': (delta, timeOfAnim) =>
				@SetModifierWeight(1, math.abs(math.sin(timeOfAnim * @GetTime() * 4)))
		}

		{
			'name': 'duck_quack'
			'autostart': false
			'repeat': false
			'time': 5
			'ids': {'Duck', 'JawOpen'}
			'create': =>
				@talkAnim = for i = 0, 1, 0.1
					rand = math.random(1, 100)
					rand > 50 and 1 or 0
				@SetModifierWeight(1, math.random(70, 90) / 100)
			'func': (delta, timeOfAnim) =>
				cPos = math.floor(timeOfAnim * 10) + 1
				data = @talkAnim[cPos]
				return if not data
				@SetModifierWeight(2, data)
		}
	}

	@SetupFlexesTables: =>
		for i, flex in pairs @FLEX_LIST
			flex.id = i - 1
			flex.targetName = "target#{flex.flex}"
		@FLEX_IDS = {flex.id, flex for _, flex in ipairs @FLEX_LIST}
		@FLEX_TABLE = {flex.flex, flex for _, flex in ipairs @FLEX_LIST}

	@SetupFlexesTables()

	@NEXT_HOOK_ID = 0
	@SequenceObject = FlexSequence

	new: (data) =>
		super(data)
		@states = [FlexState(@, flex, id, scale, speed, active) for _, {:flex, :id, :scale, :speed, :active} in ipairs @@FLEX_LIST]
		@statesTable = {state\GetFlexName(), state for _, state in ipairs @states}
		@statesTable[state\GetFlexName()\lower()] = state for _, state in ipairs @states
		@statesTable[state\GetFlexID()] = state for _, state in ipairs @states
		@RebuildIterableList()
		ponyData = data\GetData()
		flex\SetUseLerp(ponyData\GetUseFlexLerp()) for _, flex in ipairs @states
		flex\SetLerpModify(ponyData\GetFlexLerpMultiplier()) for _, flex in ipairs @states
		@Hook('PlayerStartVoice', @PlayerStartVoice)
		@Hook('PlayerEndVoice', @PlayerEndVoice)
		@ResetSequences()
		PPM2.DebugPrint('Created new flex controller for ', @GetEntity(), ' as part of ', data, '; internal ID is ', @fid)

	IsValid: => @isValid

	GetFlexState: (name = '') => @statesTable[name]
	RebuildIterableList: =>
		return false if not @isValid
		@statesIterable = [state for _, state in ipairs @states when state\GetIsActive()]
	DataChanges: (state) =>
		return if not @isValid
		flexState\DataChanges(state) for _, flexState in ipairs @states
		if state\GetKey() == 'UseFlexLerp'
			flex\SetUseLerp(state\GetValue()) for _, flex in ipairs @states
		if state\GetKey() == 'FlexLerpMultiplier'
			flex\SetLerpModify(state\GetValue()) for _, flex in ipairs @states

	GetEntity: => @controller\GetEntity()
	GetData: => @controller
	GetController: => @controller

	PlayerStartVoice: (ply = NULL) =>
		return if ply ~= @GetEntity()
		@StartSequence('talk_endless')
	PlayerEndVoice: (ply = NULL) =>
		return if ply ~= @GetEntity()
		@EndSequence('talk_endless')

	ResetSequences: =>
		super()
		state\Reset(false) for _, state in ipairs @statesIterable

	Think: (ent = @GetEntity()) =>
		return if DISABLE_FLEXES\GetBool()
		delta = super(ent)
		return if not delta
		state\Think(ent, delta) for _, state in ipairs @statesIterable
		return delta

do
	ppm2_disable_flexes = (cvar, oldval, newval) ->
		for _, ply in ipairs player.GetAll()
			data = ply\GetPonyData()
			continue if not data
			renderer = data\GetRenderController()
			continue if not renderer
			flex = renderer\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-2019 DBot

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

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

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

--
-- 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
-- 25   Male
-- 26   Hoof_Fluffers
-- 27   o3o
-- 28   Ear_Fluffers
-- 29   Fangs
-- 30   Claw_Teeth
-- 31   Fang_Test
-- 32   angry_eyes
-- 33   sad_eyes
-- 34   Eyes_Blink_Lower
-- 35   Male_2
-- 36   Buff_Body
-- 37   Manliest_Chin
-- 38   Lowerlid_Raise
-- 39   Happy_Eyes
-- 40   Duck

BLINK_CURVE = {0, 0.2, 0.45, 0.65, 0.9, 1, 1, 0.75, 0.55, 0.25, 0.15, 0}
BREATH_CURVE = {0, 0, 0, 0.1, 0.3, 0.55, 0.85, 1, 1, 0.85, 0.65, 0.4, 0.2, 0, 0, 0}

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

class FlexState extends PPM2.ModifierBase
	@SetupModifiers: =>
		@RegisterModifier('Speed', 0)
		@RegisterModifier('Scale', 0)
		@RegisterModifier('Weight', 0)

	new: (controller, flexName = '', flexID = 0, scale = 1, speed = 1, active = true, min = 0, max = 1, useModifiers = true) =>
		super()
		@controller = controller
		@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
		@modifiers = {}
		@useModifiers = useModifiers
		@active = active
		@useLerp = true
		@lerpMultiplier = 1
		@activeID = "DisableFlex#{@flexName}"

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

	GetFlexID: => @flexID
	GetFlexName: => @flexName
	SetUseLerp: (val = true) => @useLerp = val
	GetUseLerp: => @useLerp
	UseLerp: => @useLerp
	SetLerpModify: (val = 1) => @lerpMultiplier = val
	GetLerpModify: => @lerpMultiplier
	LerpModify: => @lerpMultiplier

	GetEntity: => @controller\GetEntity()
	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

	GetIsActive: => @active
	SetIsActive: (val = true) => @active = val

	AddValue: (val = 0) => @SetValue(@current + val)
	AddRealValue: (val = 0) => @SetRealValue(@target + val)
	Think: (ent = @GetEntity(), delta = 0) =>
		return if not @active

		if @useModifiers
			@current = 0
			@scale = @originalscale * @scaleModify
			@speed = @originalspeed * @speedModify

			if @useLerp
				for i = 1, #@WeightModifiers
					@modifiers[i] = Lerp(delta * 15 * @speed * @speedModify * @lerpMultiplier, @modifiers[i] or 0, @WeightModifiers[i])
					@current += @modifiers[i]
			else
				@current += @WeightModifiers[i] for i = 1, #@WeightModifiers

			@scale += modif for _, modif in ipairs @ScaleModifiers
			@speed += modif for _, modif in ipairs @SpeedModifiers
			@current = math.Clamp(@current, @min, @max) * @scale

		ent\SetFlexWeight(@flexID, @current)
	DataChanges: (state) =>
		return if state\GetKey() ~= @activeID
		@SetIsActive(not state\GetValue())
		@GetController()\RebuildIterableList()
		@Reset()
	Reset: (resetVars = true) =>
		for name, id in pairs @modifiersNames
			@ResetModifiers(name)
		if resetVars
			@scaleModify = 1
			@speedModify = 1
		@scale = @originalscale * @scaleModify
		@speed = @originalspeed * @speedModify
		@target = 0
		@current = 0
		@GetEntity()\SetFlexWeight(@flexID, 0) if IsValid(@GetEntity())

PPM2.FlexState = FlexState

class FlexSequence extends PPM2.SequenceBase
	new: (controller, data) =>
		super(controller, data)

		{
			'ids': @flexIDsIterable
			'numid': @numid
		} = data

		@flexIDS = {}
		@flexStates = {}
		i = 1
		for _, id in ipairs 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

		@controller = controller
		@Launch()

	GetController: => @controller
	GetModifierID: (id = '') => @flexIDS[id]
	GetFlexState: (id = '') => @flexStates[id]

	Think: (delta = 0) =>
		return false if not IsValid(@GetEntity())
		super(delta)

	Stop: =>
		super()
		return unless @parent
		@parent\GetFlexState(id)\ResetModifiers(@name) for _, id in ipairs @flexIDsIterable

	SetModifierWeight: (id = '', val = 0) => @GetFlexState(id)\SetModifierWeight(@GetModifierID(id), val)
	SetModifierSpeed: (id = '', val = 0) => @GetFlexState(id)\SetModifierSpeed(@GetModifierID(id), val)
	SetUseLerp: (id = '', status = true) => @GetFlexState(id)\SetUseLerp(status)
	GetUseLerp: (id = '') => @GetFlexState(id)\GetUseLerp()

PPM2.FlexSequence = FlexSequence

class PonyFlexController extends PPM2.ControllerChildren
	@AVALIABLE_CONTROLLERS = {}
	@MODELS = {'models/ppm/player_default_base_new.mdl', 'models/ppm/player_default_base_new_nj.mdl'}

	@FLEX_LIST = {
		{flex: 'eyes_updown',       scale: 1, speed: 1, active: false}
		{flex: 'eyes_rightleft',    scale: 1, speed: 1, active: false}
		{flex: 'JawOpen',           scale: 1, speed: 1, active: true}
		{flex: 'JawClose',          scale: 1, speed: 1, active: true}
		{flex: 'Smirk',             scale: 1, speed: 1, active: true}
		{flex: 'Frown',             scale: 1, speed: 1, active: true}
		{flex: 'Stretch',           scale: 1, speed: 1, active: false}
		{flex: 'Pucker',            scale: 1, speed: 1, active: false}
		{flex: 'Grin',              scale: 1, speed: 1, active: true}
		{flex: 'CatFace',           scale: 1, speed: 1, active: true}
		{flex: 'Mouth_O',           scale: 1, speed: 1, active: true}
		{flex: 'Mouth_O2',          scale: 1, speed: 1, active: true}
		{flex: 'Mouth_Full',        scale: 1, speed: 1, active: false}
		{flex: 'Tongue_Out',        scale: 1, speed: 1, active: true}
		{flex: 'Tongue_Up',         scale: 1, speed: 1, active: true}
		{flex: 'Tongue_Down',       scale: 1, speed: 1, active: true}
		{flex: 'NoEyelashes',       scale: 1, speed: 1, active: false}
		{flex: 'Eyes_Blink',        scale: 1, speed: 1, active: false}
		{flex: 'Left_Blink',        scale: 1, speed: 1, active: true}
		{flex: 'Right_Blink',       scale: 1, speed: 1, active: true}
		{flex: 'Scrunch',           scale: 1, speed: 1, active: true}
		{flex: 'FatButt',           scale: 1, speed: 1, active: false}
		{flex: 'Stomach_Out',       scale: 1, speed: 1, active: true}
		{flex: 'Stomach_In',        scale: 1, speed: 1, active: true}
		{flex: 'Throat_Bulge',      scale: 1, speed: 1, active: true}
		{flex: 'Male',              scale: 1, speed: 1, active: false}
		{flex: 'Hoof_Fluffers',     scale: 1, speed: 1, active: false}
		{flex: 'o3o',               scale: 1, speed: 1, active: true}
		{flex: 'Ear_Fluffers',      scale: 1, speed: 1, active: false}
		{flex: 'Fangs',             scale: 1, speed: 1, active: false}
		{flex: 'Claw_Teeth',        scale: 1, speed: 1, active: false}
		{flex: 'Fang_Test',         scale: 1, speed: 1, active: false}
		{flex: 'angry_eyes',        scale: 1, speed: 1, active: true}
		{flex: 'sad_eyes',          scale: 1, speed: 1, active: true}
		{flex: 'Eyes_Blink_Lower',  scale: 1, speed: 1, active: true}
		{flex: 'Male_2',            scale: 1, speed: 1, active: false}
		{flex: 'Buff_Body',         scale: 1, speed: 1, active: false}
		{flex: 'Manliest_Chin',     scale: 1, speed: 1, active: false}
		{flex: 'Lowerlid_Raise',    scale: 1, speed: 1, active: false}
		{flex: 'Happy_Eyes',        scale: 1, speed: 1, active: true}
		{flex: 'Duck',              scale: 1, speed: 1, active: true}

	}

	@SEQUENCES = {
		{
			'name': 'anger'
			'autostart': false
			'repeat': false
			'time': 5
			'ids': {'Frown', 'Grin', 'angry_eyes', 'Scrunch'}
			'reset': =>
				@SetTime(math.random(15, 45) / 10)
				@lastStrengthUpdate = @lastStrengthUpdate or 0
				if @lastStrengthUpdate < CurTimeL()
					@lastStrengthUpdate = CurTimeL() + 2
					@frownStrength = math.random(40, 100) / 100
					@grinStrength = math.random(15, 40) / 100
					@angryStrength = math.random(30, 80) / 100
					@scrunchStrength = math.random(50, 100) / 100
					@SetModifierWeight(1, @frownStrength)
					@SetModifierWeight(2, @grinStrength)
					@SetModifierWeight(3, @angryStrength)
					@SetModifierWeight(4, @scrunchStrength)
		}

		{
			'name': 'sad'
			'autostart': false
			'repeat': false
			'time': 5
			'ids': {'Frown', 'Grin', 'sad_eyes'}
			'reset': =>
				@SetTime(math.random(15, 45) / 10)
				@lastStrengthUpdate = @lastStrengthUpdate or 0
				if @lastStrengthUpdate < CurTimeL()
					@lastStrengthUpdate = CurTimeL() + 2
					@frownStrength = math.random(40, 100) / 100
					@grinStrength = math.random(15, 40) / 100
					@angryStrength = math.random(30, 80) / 100
			'func': (delta, timeOfAnim) =>
				@SetModifierWeight(1, @frownStrength)
				@SetModifierWeight(2, @grinStrength)
				@SetModifierWeight(3, @angryStrength)
		}

		{
			'name': 'ugh'
			'autostart': false
			'repeat': false
			'time': 5
			'ids': {'sad_eyes', 'Eyes_Blink_Lower'}
			'reset': =>
				@SetModifierWeight(1, math.Rand(0.27, 0.34))
				@SetModifierWeight(2, math.Rand(0.3, 0.35))
				@PauseSequence('eyes_blink')
				@PauseSequence('eyes_idle')
		}

		{
			'name': 'suggestive_eyes'
			'autostart': false
			'repeat': false
			'time': 5
			'ids': {'sad_eyes', 'Eyes_Blink_Lower'}
			'reset': =>
				@SetModifierWeight(1, 0.28)
				@SetModifierWeight(2, 0.4)
				@PauseSequence('eyes_blink')
				@PauseSequence('eyes_idle')
		}

		{
			'name': 'lips_lick'
			'autostart': false
			'repeat': false
			'time': 5
			'ids': {'Tongue_Out', 'Tongue_Up'}
			'reset': =>
				@SetModifierWeight(1, 0.9)
			'func': (delta, timeOfAnim) =>
				@SetModifierWeight(2, 0.75 + math.sin(CurTimeL() * 7) * 0.25)
		}

		{
			'name': 'tongue_pullout'
			'autostart': false
			'repeat': false
			'time': 5
			'ids': {'Tongue_Out'}
			'func': (delta, timeOfAnim) =>
				@SetModifierWeight(1, 0.15 + math.sin(CurTimeL() * 10) * 0.1)
		}

		{
			'name': 'tongue_pullout_twitch'
			'autostart': false
			'repeat': false
			'time': 5
			'ids': {'Tongue_Out'}
			'func': (delta, timeOfAnim) =>
				@SetModifierWeight(1, 0.5 + math.sin(CurTimeL() * 4) * 0.5)
		}

		{
			'name': 'tongue_pullout_twitch_fast'
			'autostart': false
			'repeat': false
			'time': 5
			'ids': {'Tongue_Out'}
			'func': (delta, timeOfAnim) =>
				@SetModifierWeight(1, 0.5 + math.sin(CurTimeL() * 8) * 0.5)
		}

		{
			'name': 'suggestive_open'
			'autostart': false
			'repeat': false
			'time': 5
			'ids': {'Pucker', 'JawOpen', 'Scrunch'}
			'reset': =>
				@SetModifierWeight(1, math.Rand(0.28, 0.34))
				@SetModifierWeight(2, math.Rand(0.35, 0.40))
				@SetModifierWeight(3, math.Rand(0.45, 0.50))
		}

		{
			'name': 'suggestive_open_anim'
			'autostart': false
			'repeat': false
			'time': 5
			'ids': {'Pucker', 'JawOpen', 'Scrunch'}
			'reset': =>
				@SetModifierWeight(1, math.Rand(0.28, 0.34))
				@SetModifierWeight(3, math.Rand(0.45, 0.50))
			'func': (delta, timeOfAnim) =>
				@SetModifierWeight(2, 0.2 + math.sin(CurTimeL() * 16) * 0.07)
		}

		{
			'name': 'face_smirk'
			'autostart': false
			'repeat': false
			'time': 5
			'ids': {'Smirk', 'Frown'}
			'reset': =>
				@SetModifierWeight(1, 0.78)
				@SetModifierWeight(2, 0.61)
		}

		{
			'name': 'eyes_idle'
			'autostart': true
			'repeat': true
			'time': 5
			'ids': {'Left_Blink', 'Right_Blink'}
			'func': (delta, timeOfAnim) =>
				return false if @GetEntity()\GetNWBool('PPM2.IsDeathRagdoll')
				value = math.abs(math.sin(CurTimeL() * .5) * .15)
				@SetModifierWeight(1, value)
				@SetModifierWeight(2, value)
		}

		{
			'name': 'eyes_close'
			'autostart': true
			'repeat': true
			'time': 5
			'ids': {'Left_Blink', 'Right_Blink', 'Frown'}
			'func': (delta, timeOfAnim) =>
				return if not @GetEntity()\GetNWBool('PPM2.IsDeathRagdoll')
				@SetModifierWeight(1, 1)
				@SetModifierWeight(2, 1)
				@SetModifierWeight(3, 0.5)
		}

		{
			'name': 'body_idle'
			'autostart': true
			'repeat': true
			'time': 2
			'ids': {'Stomach_Out'}
			'func': (delta, timeOfAnim) =>
				return false if @GetEntity()\GetNWBool('PPM2.IsDeathRagdoll')
				return if timeOfAnim < 0 or timeOfAnim > 1
				@SetModifierWeight(1, math.tbezier(timeOfAnim, BREATH_CURVE) * 0.35)
		}

		{
			'name': 'health_idle'
			'autostart': true
			'repeat': true
			'time': 5
			'ids': {'Frown', 'Left_Blink', 'Right_Blink', 'Scrunch', 'Mouth_O', 'JawOpen', 'Grin'}
			'func': (delta, timeOfAnim) =>
				return false if not @GetEntity()\IsPlayer() and not @GetEntity()\IsNPC() and @GetEntity().Type ~= 'nextbot'
				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 = @GetEntity()\Health(), @GetEntity()\GetMaxHealth()
				mhp = 1 if mhp == 0
				div = hp / mhp
				strength = math.Clamp(1.5 - div * 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(CurTimeL() * strength * 3) * .1)
				else
					JawOpenState\SetModifierWeight(JawOpen, 0)

				if div >= 2
					@SetModifierWeight(7, .5)
				else
					@SetModifierWeight(7, 0)
		}

		{
			'name': 'greeny'
			'autostart': false
			'repeat': false
			'time': 2
			'ids': {'Grin'}
			'func': (delta, timeOfAnim) =>
				Grin = @GetModifierID(1)
				GrinState = @GetFlexState(1)
				strength = .5 + math.sin(CurTimeL() * 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': 'o3o'
			'autostart': false
			'repeat': false
			'time': 3
			'ids': {'o3o'}
			'func': (delta, timeOfAnim) =>
				@SetModifierWeight(1, 1)
		}

		{
			'name': 'owo_alternative'
			'autostart': false
			'repeat': false
			'time': 5
			'ids': {'o3o', 'JawOpen'}
			'reset': (delta, timeOfAnim) =>
				@SetModifierWeight(1, math.Rand(0.8, 1))
				@SetModifierWeight(2, math.Rand(0.05, 0.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': 'angry_tongue'
			'autostart': false
			'repeat': false
			'time': 6
			'ids': {'Frown', 'Grin', 'angry_eyes', 'Scrunch', 'JawOpen', 'Tongue_Out'}
			'reset': (delta, timeOfAnim) =>
				@SetModifierWeight(1, math.random(40, 100) / 100)
				@SetModifierWeight(2, math.random(15, 40) / 100)
				@SetModifierWeight(3, math.random(30, 80) / 100)
				@SetModifierWeight(4, math.random(50, 100) / 100)
				@SetModifierWeight(5, math.random(10, 15) / 100)
				@SetModifierWeight(6, math.random(80, 100) / 100)
		}

		{
			'name': 'pffff'
			'autostart': false
			'repeat': false
			'time': 6
			'ids': {'Frown', 'Grin', 'angry_eyes', 'Scrunch', 'JawOpen', 'Tongue_Out', 'Tongue_Down', 'Tongue_Up'}
			'reset': =>
				@SetModifierWeight(1, math.random(40, 100) / 100)
				@SetModifierWeight(2, math.random(15, 40) / 100)
				@SetModifierWeight(3, math.random(30, 80) / 100)
				@SetModifierWeight(4, math.random(50, 100) / 100)
				@SetModifierWeight(5, math.random(10, 15) / 100)
				@SetModifierWeight(6, math.random(80, 100) / 100)
			'func': (delta, timeOfAnim) =>
				val = math.sin(CurTimeL() * 8) * .6
				if val > 0
					@SetModifierWeight(7, val)
					@SetModifierWeight(8, 0)
				else
					@SetModifierWeight(7, 0)
					@SetModifierWeight(8, -val)
		}

		{
			'name': 'cat'
			'autostart': false
			'repeat': false
			'time': 5
			'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}
				@SetModifierSpeed(1, 2)
				@SetModifierSpeed(2, 2)
				@SetModifierSpeed(3, 2)
				@SetModifierSpeed(4, 2)
			'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}
				@SetModifierSpeed(1, 2)
				@SetModifierSpeed(2, 2)
				@SetModifierSpeed(3, 2)
				@SetModifierSpeed(4, 2)
			'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 = @GetEntity()\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': {'Eyes_Blink_Lower'}
			'create': =>
				@SetUseLerp(1, false)

			'reset': =>
				@nextBlink = math.random(300, 600) / 1000
				@nextBlinkLength = math.random(15, 30) / 800
				@min, @max = @nextBlink, @nextBlink + @nextBlinkLength

			'func': (delta, timeOfAnim) =>
				return false if @GetEntity()\GetNWBool('PPM2.IsDeathRagdoll')
				if @min > timeOfAnim or @max < timeOfAnim
					if @blinkHit
						@blinkHit = false
						@SetModifierWeight(1, 0)

					return

				len = (timeOfAnim - @min) / @nextBlinkLength
				blink = math.tbezier(len, BLINK_CURVE)

				@SetModifierWeight(1, blink)
				-- print(len, blink)

				@blinkHit = true
		}

		{
			'name': 'hurt'
			'autostart': false
			'repeat': false
			'time': 4
			'ids': {'JawOpen', 'Frown', 'Grin', 'Scrunch'}
			'reset': (delta, timeOfAnim) =>
				@SetModifierWeight(1, math.random(4, 16) / 100)
				@SetModifierWeight(2, math.random(60, 70) / 100)
				@SetModifierWeight(3, math.random(30, 40) / 100)
				@SetModifierWeight(4, math.random(70, 90) / 100)
		}

		{
			'name': 'kill_grin'
			'autostart': false
			'repeat': false
			'time': 8
			'ids': {'Smirk', 'Frown', 'Grin'}
			'func': (delta, timeOfAnim) =>
				@SetModifierWeight(1, .51)
				@SetModifierWeight(2, .38)
				@SetModifierWeight(3, .66)
		}

		{
			'name': 'sorry'
			'autostart': false
			'repeat': false
			'time': 8
			'ids': {'Frown', 'Stretch', 'Grin', 'Scrunch', 'sad_eyes'}
			'create': =>
				@SetModifierWeight(1, math.random(45, 75) / 100)
				@SetModifierWeight(2, math.random(45, 75) / 100)
				@SetModifierWeight(3, math.random(70, 100) / 100)
				@SetModifierWeight(4, math.random(7090, 100) / 100)
		}

		{
			'name': 'scrunch'
			'autostart': false
			'repeat': false
			'time': 6
			'ids': {'Scrunch'}
			'create': =>
				@SetModifierWeight(1, math.random(80, 100) / 100)
		}

		{
			'name': 'gulp'
			'autostart': false
			'repeat': false
			'time': 2
			'ids': {'Throat_Bulge', 'Frown', 'Grin'}
			'create': =>
				@SetModifierWeight(2, 1)
				@SetModifierWeight(3, math.random(35, 55) / 100)
			'func': (delta, timeOfAnim) =>
				if timeOfAnim > 0.5
					@SetModifierWeight(1, (1 - timeOfAnim) * 2)
				else
					@SetModifierWeight(1, timeOfAnim * 2)
		}

		{
			'name': 'blahblah'
			'autostart': false
			'repeat': false
			'time': 3
			'ids': {'o3o', 'Mouth_O'}
			'create': =>
				@SetModifierWeight(1, 1)
				@talkAnim = [math.random(50, 70) / 100 for i = 0, 1, 0.05]
			'func': (delta, timeOfAnim) =>
				cPos = math.floor(timeOfAnim * 20) + 1
				data = @talkAnim[cPos]
				return if not data
				@SetModifierWeight(2, data)
		}

		{
			'name': 'wink_left'
			'autostart': false
			'repeat': false
			'time': 2
			'ids': {'Frown', 'Stretch', 'Grin', 'Left_Blink'}
			'create': =>
				@SetModifierWeight(1, math.random(40, 60) / 100)
				@SetModifierWeight(2, math.random(30, 50) / 100)
				@SetModifierWeight(3, math.random(60, 100) / 100)
				@SetModifierWeight(4, 1)
				@PauseSequence('eyes_blink')
		}

		{
			'name': 'wink_right'
			'autostart': false
			'repeat': false
			'time': 2
			'ids': {'Frown', 'Stretch', 'Grin', 'Right_Blink'}
			'create': =>
				@SetModifierWeight(1, math.random(40, 60) / 100)
				@SetModifierWeight(2, math.random(30, 50) / 100)
				@SetModifierWeight(3, math.random(60, 100) / 100)
				@SetModifierWeight(4, 1)
				@PauseSequence('eyes_blink')
		}

		{
			'name': 'happy_eyes'
			'autostart': false
			'repeat': false
			'time': 3
			'ids': {'Happy_Eyes'}
			'create': =>
				@SetModifierWeight(1, 1)
				@PauseSequence('eyes_blink')
		}

		{
			'name': 'happy_grin'
			'autostart': false
			'repeat': false
			'time': 3
			'ids': {'Happy_Eyes', 'Grin'}
			'create': =>
				@SetModifierWeight(1, 1)
				@SetModifierWeight(2, 1)
				@PauseSequence('eyes_blink')
		}

		{
			'name': 'duck'
			'autostart': false
			'repeat': false
			'time': 3
			'ids': {'Duck'}
			'create': =>
				@SetModifierWeight(1, math.random(70, 90) / 100)
		}

		{
			'name': 'duck_insanity'
			'autostart': false
			'repeat': false
			'time': 3
			'ids': {'Duck'}
			'func': (delta, timeOfAnim) =>
				@SetModifierWeight(1, math.abs(math.sin(timeOfAnim * @GetTime() * 4)))
		}

		{
			'name': 'duck_quack'
			'autostart': false
			'repeat': false
			'time': 5
			'ids': {'Duck', 'JawOpen'}
			'create': =>
				@talkAnim = for i = 0, 1, 0.1
					rand = math.random(1, 100)
					rand > 50 and 1 or 0
				@SetModifierWeight(1, math.random(70, 90) / 100)
			'func': (delta, timeOfAnim) =>
				cPos = math.floor(timeOfAnim * 10) + 1
				data = @talkAnim[cPos]
				return if not data
				@SetModifierWeight(2, data)
		}
	}

	@SetupFlexesTables: =>
		for i, flex in pairs @FLEX_LIST
			flex.id = i - 1
			flex.targetName = "target#{flex.flex}"
		@FLEX_IDS = {flex.id, flex for _, flex in ipairs @FLEX_LIST}
		@FLEX_TABLE = {flex.flex, flex for _, flex in ipairs @FLEX_LIST}

	@SetupFlexesTables()

	@NEXT_HOOK_ID = 0
	@SequenceObject = FlexSequence

	new: (data) =>
		super(data)
		@states = [FlexState(@, flex, id, scale, speed, active) for _, {:flex, :id, :scale, :speed, :active} in ipairs @@FLEX_LIST]
		@statesTable = {state\GetFlexName(), state for _, state in ipairs @states}
		@statesTable[state\GetFlexName()\lower()] = state for _, state in ipairs @states
		@statesTable[state\GetFlexID()] = state for _, state in ipairs @states
		@RebuildIterableList()
		ponyData = data\GetData()
		flex\SetUseLerp(ponyData\GetUseFlexLerp()) for _, flex in ipairs @states
		flex\SetLerpModify(ponyData\GetFlexLerpMultiplier()) for _, flex in ipairs @states
		@Hook('PlayerStartVoice', @PlayerStartVoice)
		@Hook('PlayerEndVoice', @PlayerEndVoice)
		@ResetSequences()
		PPM2.DebugPrint('Created new flex controller for ', @GetEntity(), ' as part of ', data, '; internal ID is ', @fid)

	IsValid: => @isValid

	GetFlexState: (name = '') => @statesTable[name]
	RebuildIterableList: =>
		return false if not @isValid
		@statesIterable = [state for _, state in ipairs @states when state\GetIsActive()]
	DataChanges: (state) =>
		return if not @isValid
		flexState\DataChanges(state) for _, flexState in ipairs @states
		if state\GetKey() == 'UseFlexLerp'
			flex\SetUseLerp(state\GetValue()) for _, flex in ipairs @states
		if state\GetKey() == 'FlexLerpMultiplier'
			flex\SetLerpModify(state\GetValue()) for _, flex in ipairs @states

	GetEntity: => @controller\GetEntity()
	GetData: => @controller
	GetController: => @controller

	PlayerStartVoice: (ply = NULL) =>
		return if ply ~= @GetEntity()
		@StartSequence('talk_endless')
	PlayerEndVoice: (ply = NULL) =>
		return if ply ~= @GetEntity()
		@EndSequence('talk_endless')

	ResetSequences: =>
		super()
		state\Reset(false) for _, state in ipairs @statesIterable

	Think: (ent = @GetEntity()) =>
		return if DISABLE_FLEXES\GetBool()
		delta = super(ent)
		return if not delta
		state\Think(ent, delta) for _, state in ipairs @statesIterable
		return delta

do
	ppm2_disable_flexes = (cvar, oldval, newval) ->
		for _, ply in ipairs player.GetAll()
			data = ply\GetPonyData()
			continue if not data
			renderer = data\GetRenderController()
			continue if not renderer
			flex = renderer\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]


Stub for utils.Input.define

Stub for utils.Input.define

export ^

class input
	enable: ->
	define: (value) ->

	-- key is either a string (from Key or the Löve KeyConstants) or a table of strings
	check: (key) ->
		return false if key == nil
		switch type key
			when "string"
				love.keyboard.isDown key
			when "table"
				love.keyboard.isDown unpack key
			else
				print "Unknown key type", type key

Input = input!

export ^

class input
	enable: ->

	-- key is either a string (from Key or the Löve KeyConstants) or a table of strings
	check: (key) ->
		return false if key == nil
		switch type key
			when "string"
				love.keyboard.isDown key
			when "table"
				love.keyboard.isDown unpack key
			else
				print "Unknown key type", type key

Input = input!