utf8: use say, add flip as alias

utf8: use say, add flip as alias

Former-commit-id: 864a92384fa0bdb4c69f329d6133c9784e5697a1 [formerly 231869d219854fd67cb4341718f43c149e1eee30]
Former-commit-id: 85c679c489adfbceb865ffcd8ad88bffbc4e66d7

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

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

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

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

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

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

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

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

    table.concat(t, "")

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

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

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

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

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

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

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

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

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

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

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

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

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

    table.concat(t, "")

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

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

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

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

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


Fix colors

Fix colors

import java, Font, Image, Quad from sauce3

Constants = require "sauce3/wrappers"
Color     = java.require "com.badlogic.gdx.graphics.Color"
Gdx       = java.require "com.badlogic.gdx.Gdx"
GL20      = java.require "com.badlogic.gdx.graphics.GL20"
Matrix4   = java.require "com.badlogic.gdx.math.Matrix4"
Sauce3VM   = java.require "sauce3.Sauce3VM"

OrthographicCamera = java.require "com.badlogic.gdx.graphics.OrthographicCamera"
ShapeRender        = java.require "com.badlogic.gdx.graphics.glutils.ShapeRenderer"
SpriteBatch        = java.require "com.badlogic.gdx.graphics.g2d.SpriteBatch"

shader = SpriteBatch\createDefaultShader!

batch  = java.new SpriteBatch, 1000, shader
shapes = java.new ShapeRender

font = Font!

shapes\setAutoShapeType true

matrix = java.new Matrix4
color  = java.new Color, 1, 1, 1, 1

background = java.new Color, 0, 0, 0, 1
blending   = "alpha"

batch\setColor color
shapes\setColor color
font.font\setColor color

camera = java.new OrthographicCamera
camera\setToOrtho true

batch\setProjectionMatrix camera.combined
shapes\setProjectionMatrix camera.combined

matrix_dirty = false

check = (texture_based) ->
  if texture_based
    if matrix_dirty
      batch\setTransformMatrix matrix
      matrix_dirty = false

    if shapes\isDrawing!
      Sauce3VM.util\endShapes shapes
    unless batch\isDrawing!
      batch\begin!
  else
    if matrix_dirty
      shapes\setTransformMatrix matrix
      matrix_dirty = false

    if batch\isDrawing!
      Sauce3VM.util\endBatch batch
    unless shapes\isDrawing!
      shapes\begin!

arc = (mode, x, y, radius, a1, a2) ->
  check false

  shapes\set Constants.shapes[mode]
  shapes\arc x, y, radius, (math.deg a1), math.deg a2

circle = (mode, x, y, radius) ->
  check false

  shapes\set Constants.shapes[mode]
  shapes\circle x, y, radius

clear = ->
  Gdx.gl\glClearColor background.r, background.g, background.b
  Gdx.gl\glClear GL20.GL_COLOR_BUFFER_BIT

draw = (image, x = 0, y = 0, r = 0, sx = 1, sy = 1, ox = 0, oy = 0) ->
  check true

  w = image\getWidth!
  h = image\getHeight!

  src_x = 0
  src_y = 0

  src_w = w
  src_h = h

  x -= ox
  y -= oy

  batch\draw image.texture, x, y, ox, oy, w, h, sx, sy, r, srx_x, srx_y, src_w, src_h, false, true

draw_q = (image, quad, x = 0, y = 0, r = 0, sx = 1, sy = 1, ox = 0, oy = 0) ->
  check true

  w = quad.width
  h = quad.height

  src_x = quad.x
  src_y = quad.y

  src_w = quad.sw
  src_h = quad.sh

  x -= ox
  y -= oy

  batch\draw image.texture, x, y, ox, oy, w, h, sx, sy, r, src_x, src_y, src_w, src_h, false, true

ellipse = (mode, x, y, width, height) ->
  check false

  shapes\set Constants.shapes[mode]
  shapes\ellipse x, y, width, height

line = (x1, y1, x2, y2) ->
  check false
  shapes\line x1, y1, x2, y2

origin = ->
  matrix\idt!
  matrix_dirty = true

point = (x, y) ->
  check false
  shapes\point x, y, 0

polygon = (mode, ...) ->
  check false
  shapes\set Constants.shapes[mode]

  args = table.pack ...

  if type args[1] == "table"
    shapes\polygon args[1]
  else
    shapes\polygon args

present = ->
  if shapes\isDrawing!
    Sauce3VM.util\endShapes shapes
  if batch\isDrawing!
    Sauce3VM.util\endBatch batch

print = (text, x = 0, y = 0, r = 0, sx = 1, sy = 1, ox = 0, oy = 0) ->
  check true

  local tmp

  unless r == 0
    tmp = batch\getTransformMatrix!
    translate x, y
    rotate r
    translate -x, -y

    batch\setTransformMatrix matrix
    matrix_dirty = false

  font.font\getData!\setScale sx, -sy
  font.font\draw batch, text, x - ox * sx, y - oy * sy

  unless r == 0
    translate x, y
    rotate -r
    translate -x, -y

    batch\setTransformMatrix tmp
    matrix_dirty = false

print_f = (text, width = 0, align = "left", x = 0, y = 0, r = 0, sx = 1, sy = 1, ox = 0, oy = 0) ->
  check true

  tmp = nil

  unless r == 0
    tmp = batch\getTransformMatrix!
    translate x, y
    rotate r
    translate -x, -y

    batch\setTransformMatrix matrix
    matrix_dirty = false

  font.font\getData!\setScale sx, -sy
  font.font\draw batch, text, x - ox * sx, y - oy * sy, width, Constants.aligns[align], true

  unless r == 0
    translate x, y
    rotate -r
    translate -x, -y

    batch\setTransformMatrix tmp
    matrix_dirty = false

rectangle = (mode, x, y, width, height) ->
  check false

  shapes\set Constants.shapes[mode]
  shapes\rect x, y, width, height

reset = ->
  shader = SpriteBatch\createDefaultShader!
  batch\setShader shader

  background\set 0.4, 0.3, 0.4, 1
  color\set 1, 1, 1, 1

  batch\setColor color
  shapes\setColor color
  font.font\setColor color

  matrix\idt!
  matrix_dirty = true

translate = (x, y) ->
  matrix\translate x, y
  matrix_dirty = true

rotate = (radians) ->
  matrix\rotate 0, 0, 1, math.deg radians
  matrix_dirty = true

scale = (sx, sy) ->
  matrix\scale sx, sy, 1
  matrix_dirty = true

get_background_color = ->
  background.r * 255, background.g * 255, background.b * 255

get_blend_mode = ->
  blending

get_color = ->
  color.r * 255, color.g * 255, color.b * 255, color.a * 255

get_font = ->
  font

set_background_color = (r, g, b, a = 255) ->
  background\set r / 255, g / 255, b / 255, a / 255

set_blend_mode = (mode) ->
  blending = mode

  blend_mode = Constants[blending]
  batch\setBlendFunction blend_mode[1], blend_mode[2]

set_color = (r, g, b, a = 255) ->
  color\set r / 255, g / 255, b / 255, a / 255

  batch\setColor color
  shapes\setColor color
  font.font\setColor color

set_font = (v) ->
  font = v

set_new_font = (path, size, f_type) ->
  font = new_font path, size, f_type

new_font = (path, size, f_type) ->
  (Font path, size, f_type)

new_image = (path, format, f_type) ->
  (Image path, format, f_type)

new_quad = (x, y, width, height, sx, sy) ->
  (Quad x, y, width, height, sx, sy)

{
  :arc
  :circle
  :clear
  :draw
  :draw_q
  :ellipse
  :get_background_color
  :get_blendMode
  :get_color
  :get_font
  :line
  :origin
  :point
  :polygon
  :present
  :print
  :print_f
  :rectangle
  :reset
  :translate
  :rotate
  :scale
  :set_background_color
  :set_blend_mode
  :set_color
  :set_font
  :set_new_font
  :new_font
  :new_image
  :new_quad
}

import java, Font, Image, Quad from sauce3

Constants = require "sauce3/wrappers"
Color     = java.require "com.badlogic.gdx.graphics.Color"
Gdx       = java.require "com.badlogic.gdx.Gdx"
GL20      = java.require "com.badlogic.gdx.graphics.GL20"
Matrix4   = java.require "com.badlogic.gdx.math.Matrix4"
Sauce3VM   = java.require "sauce3.Sauce3VM"

OrthographicCamera = java.require "com.badlogic.gdx.graphics.OrthographicCamera"
ShapeRender        = java.require "com.badlogic.gdx.graphics.glutils.ShapeRenderer"
SpriteBatch        = java.require "com.badlogic.gdx.graphics.g2d.SpriteBatch"

shader = SpriteBatch\createDefaultShader!

batch  = java.new SpriteBatch, 1000, shader
shapes = java.new ShapeRender

font = Font!

shapes\setAutoShapeType true

matrix = java.new Matrix4
color  = java.new Color, 1, 1, 1, 1

background = java.new Color, 0, 0, 0, 1
blending   = "alpha"

batch\setColor color
shapes\setColor color
font.font\setColor color

camera = java.new OrthographicCamera
camera\setToOrtho true

batch\setProjectionMatrix camera.combined
shapes\setProjectionMatrix camera.combined

matrix_dirty = false

check = (texture_based) ->
  if texture_based
    if matrix_dirty
      batch\setTransformMatrix matrix
      matrix_dirty = false

    if shapes\isDrawing!
      Sauce3VM.util\endShapes shapes
    unless batch\isDrawing!
      batch\begin!
  else
    if matrix_dirty
      shapes\setTransformMatrix matrix
      matrix_dirty = false

    if batch\isDrawing!
      Sauce3VM.util\endBatch batch
    unless shapes\isDrawing!
      shapes\begin!

arc = (mode, x, y, radius, a1, a2) ->
  check false

  shapes\set Constants.shapes[mode]
  shapes\arc x, y, radius, (math.deg a1), math.deg a2

circle = (mode, x, y, radius) ->
  check false

  shapes\set Constants.shapes[mode]
  shapes\circle x, y, radius

clear = ->
  Gdx.gl\glClearColor background.r, background.g, background.b
  Gdx.gl\glClear GL20.GL_COLOR_BUFFER_BIT

draw = (image, x = 0, y = 0, r = 0, sx = 1, sy = 1, ox = 0, oy = 0) ->
  check true

  w = image\getWidth!
  h = image\getHeight!

  src_x = 0
  src_y = 0

  src_w = w
  src_h = h

  x -= ox
  y -= oy

  batch\draw image.texture, x, y, ox, oy, w, h, sx, sy, r, srx_x, srx_y, src_w, src_h, false, true

draw_q = (image, quad, x = 0, y = 0, r = 0, sx = 1, sy = 1, ox = 0, oy = 0) ->
  check true

  w = quad.width
  h = quad.height

  src_x = quad.x
  src_y = quad.y

  src_w = quad.sw
  src_h = quad.sh

  x -= ox
  y -= oy

  batch\draw image.texture, x, y, ox, oy, w, h, sx, sy, r, src_x, src_y, src_w, src_h, false, true

ellipse = (mode, x, y, width, height) ->
  check false

  shapes\set Constants.shapes[mode]
  shapes\ellipse x, y, width, height

line = (x1, y1, x2, y2) ->
  check false
  shapes\line x1, y1, x2, y2

origin = ->
  matrix\idt!
  matrix_dirty = true

point = (x, y) ->
  check false
  shapes\point x, y, 0

polygon = (mode, ...) ->
  check false
  shapes\set Constants.shapes[mode]

  args = table.pack ...

  if type args[1] == "table"
    shapes\polygon args[1]
  else
    shapes\polygon args

present = ->
  if shapes\isDrawing!
    Sauce3VM.util\endShapes shapes
  if batch\isDrawing!
    Sauce3VM.util\endBatch batch

print = (text, x = 0, y = 0, r = 0, sx = 1, sy = 1, ox = 0, oy = 0) ->
  check true

  local tmp

  unless r == 0
    tmp = batch\getTransformMatrix!
    translate x, y
    rotate r
    translate -x, -y

    batch\setTransformMatrix matrix
    matrix_dirty = false

  font.font\getData!\setScale sx, -sy
  font.font\draw batch, text, x - ox * sx, y - oy * sy

  unless r == 0
    translate x, y
    rotate -r
    translate -x, -y

    batch\setTransformMatrix tmp
    matrix_dirty = false

print_f = (text, width = 0, align = "left", x = 0, y = 0, r = 0, sx = 1, sy = 1, ox = 0, oy = 0) ->
  check true

  tmp = nil

  unless r == 0
    tmp = batch\getTransformMatrix!
    translate x, y
    rotate r
    translate -x, -y

    batch\setTransformMatrix matrix
    matrix_dirty = false

  font.font\getData!\setScale sx, -sy
  font.font\draw batch, text, x - ox * sx, y - oy * sy, width, Constants.aligns[align], true

  unless r == 0
    translate x, y
    rotate -r
    translate -x, -y

    batch\setTransformMatrix tmp
    matrix_dirty = false

rectangle = (mode, x, y, width, height) ->
  check false

  shapes\set Constants.shapes[mode]
  shapes\rect x, y, width, height

reset = ->
  shader = SpriteBatch\createDefaultShader!
  batch\setShader shader

  background\set 0.4, 0.3, 0.4, 1
  color\set 1, 1, 1, 1

  batch\setColor color
  shapes\setColor color
  font.font\setColor color

  matrix\idt!
  matrix_dirty = true

translate = (x, y) ->
  matrix\translate x, y
  matrix_dirty = true

rotate = (radians) ->
  matrix\rotate 0, 0, 1, math.deg radians
  matrix_dirty = true

scale = (sx, sy) ->
  matrix\scale sx, sy, 1
  matrix_dirty = true

get_background_color = ->
  background.r * 255, background.g * 255, background.b * 255

get_blend_mode = ->
  blending

get_color = ->
  color.r * 255, color.g * 255, color.b * 255, color.a * 255

get_font = ->
  font

set_background_color = (r, g, b, a = 255) ->
  background\set r / 255, g / 255, b / 255, a / 255

set_blend_mode = (mode) ->
  blending = mode

  blend_mode = Constants[blending]
  batch\setBlendFunction blend_mode[1], blend_mode[2]

set_color = (r, g, b, a = 255) ->
  color\set r / 255, g / 255, b / 255, a / 255

set_font = (v) ->
  font = v

set_new_font = (path, size, f_type) ->
  font = new_font path, size, f_type

new_font = (path, size, f_type) ->
  (Font path, size, f_type)

new_image = (path, format, f_type) ->
  (Image path, format, f_type)

new_quad = (x, y, width, height, sx, sy) ->
  (Quad x, y, width, height, sx, sy)

{
  :arc
  :circle
  :clear
  :draw
  :draw_q
  :ellipse
  :get_background_color
  :get_blendMode
  :get_color
  :get_font
  :line
  :origin
  :point
  :polygon
  :present
  :print
  :print_f
  :rectangle
  :reset
  :translate
  :rotate
  :scale
  :set_background_color
  :set_blend_mode
  :set_color
  :set_font
  :set_new_font
  :new_font
  :new_image
  :new_quad
}


Fix imports

Fix imports

tiny = require "sauce3/framework/libs/tiny"

require_all = (...) ->
  tiny.requireAll ...

require_any = (...) ->
  tiny.requireAny ...

reject_all = (...) ->
  tiny.rejectAll ...

reject_any = (...) ->
  tiny.rejectAny ...

filter = (pattern) ->
  tiny.filter pattern

system = (t) ->
  tiny.system t

processing_system = (t) ->
  tiny.processingSystem t

sorted_system = (t) ->
  tiny.sortedSystem t

sorted_processing_system = (t) ->
  tiny.sortedProcessingSystem t

world = (...) ->
  tiny.world ...

add_entity = (world, entity) ->
  tiny.addEntity world, entity

add_system = (world, system) ->
  tiny.addSystem world, system

add = (world, ...) ->
  tiny.add world, ...

remove_entity = (world, entity) ->
  tiny.removeEntity world, entity

remove_system = (world, system) ->
  tiny.removeSystem world, system

remove = (world, ...) ->
  tiny.remove world, ...

refresh = (world) ->
  tiny.refresh world

update = (world, dt, filter) ->
  tiny.update world, dt, filter

clear_entities = (world) ->
  tiny.clearEntities world

clear_systems = (world) ->
  tiny.clearSystems world

get_entity_count = (world) ->
  tiny.getEntityCount world

get_system_count = (world) ->
  tiny.getSystemCount world

set_system_index = (world, system, index) ->
  tiny.set_system_index world, system, index

{
  :require_all
  :require_any

  :reject_all
  :reject_any

  :filter

  :system
  :sorted_system
  :processing_system
  :sorted_processing_system

  :world

  :add_entity
  :add_system
  :add

  :remove_entity
  :remove_system
  :remove

  :refresh
  :update

  :clear_entities
  :clear_systems

  :get_entity_count
  :get_system_count

  :set_system_index
}

import tiny from require "sauce3/framework/libs/tiny"

require_all = (...) ->
  tiny.requireAll ...

require_any = (...) ->
  tiny.requireAny ...

reject_all = (...) ->
  tiny.rejectAll ...

reject_any = (...) ->
  tiny.rejectAny ...

filter = (pattern) ->
  tiny.filter pattern

system = (t) ->
  tiny.system t

processing_system = (t) ->
  tiny.processingSystem t

sorted_system = (t) ->
  tiny.sortedSystem t

sorted_processing_system = (t) ->
  tiny.sortedProcessingSystem t

world = (...) ->
  tiny.world ...

add_entity = (world, entity) ->
  tiny.addEntity world, entity

add_system = (world, system) ->
  tiny.addSystem world, system

add = (world, ...) ->
  tiny.add world, ...

remove_entity = (world, entity) ->
  tiny.removeEntity world, entity

remove_system = (world, system) ->
  tiny.removeSystem world, system

remove = (world, ...) ->
  tiny.remove world, ...

refresh = (world) ->
  tiny.refresh world

update = (world, dt, filter) ->
  tiny.update world, dt, filter

clear_entities = (world) ->
  tiny.clearEntities world

clear_systems = (world) ->
  tiny.clearSystems world

get_entity_count = (world) ->
  tiny.getEntityCount world

get_system_count = (world) ->
  tiny.getSystemCount world

set_system_index = (world, system, index) ->
  tiny.set_system_index world, system, index

{
  :require_all
  :require_any

  :reject_all
  :reject_any

  :filter

  :system
  :sorted_system
  :processing_system
  :sorted_processing_system

  :world

  :add_entity
  :add_system
  :add

  :remove_entity
  :remove_system
  :remove

  :refresh
  :update

  :clear_entities
  :clear_systems

  :get_entity_count
  :get_system_count

  :set_system_index
}


Implement LP.renderMode

Implement LP.renderMode

require "LunoPunk.geometry.Point"
require "LunoPunk.geometry.Rectangle"
require "LunoPunk.geometry.Matrix"
require "LunoPunk.Scene"
require "LunoPunk.Tweener"

export ^

class lp
	-- Private
	BASELAYER = 10
	DEG = -180 / math.pi
	INT_MAX_VALUE = nil
	NUMBER_MAX_VALUE = nil
	RAD = math.pi / -180
	VERSION = "0.0.1"
	__scene = nil

	_gameTime: nil
	_renderTime: nil
	_systemTime: nil
	_updateTime: nil
	assignedFrameRate: nil
	blackColor: 0x00000000
	bounds: nil
	buffer: nil
	camera: Point!
	console: nil
	defaultFont: "default"
	elapsed: 0
	engine: nil
	entity: nil
	fixed: false
	focused: nil
	frameRate: 0
	log: nil
	matrix: Matrix!
	orientations: nil
	pan: nil
	point: Point!
	point2: Point!
	random: -> math.random!
	randomSeed: nil
	rate: 0
	rect: Rectangle!
	renderMode = (value) ->
		return __renderMode if value == nil or __renderMode == value
		__renderMode = value

		-- recreate screen for buffer rendering
		if LP.screen == nil
			LP.screen = Screen!
		else
			LP.screen.init!

		value

	scene: Scene!
	screen: nil
	sprite: nil
	tweener: Tweener!
	volume: nil
	watch: nil
	world: nil
	zero: Point!

	-- Window/resolution related fields
	fullscreen: false
	halfHeight: -> LP.height/2
	halfWidth: -> LP.width/2
	height: 0
	width: 0
	windowHeight: 0
	windowWidth: 0

	alarm: (delay, complete, typeweenType, tweener) ->
	anchorTo: (object, anchor, distance) ->
	angle: (x1, y1, x2, y2) ->
	angleDifference: (angle1, angle2) ->
	angleXY: (object, angle, length, x, y) ->
	approach: (value, target, amount) ->
	choose: (objs) ->
	clamp: (value, min, max) -> if value < min
			min
		else if value > max
			max
		else
			value
	clampInRect: (object, x, y, width, height, padding) ->
	clear: (t) -> while #t > 0 do table.remove t
	colorLerp: (fromColor, toColor, t) ->
	consoleEnabled: ->
	createBitmap: (width, height, transparent, color) ->
	distance: (x1, y1, x2, y2) ->
	istanceRectPoint: (px, py, rx, ry, rw, rh) ->
	distanceRects: (x1, y1, w1, h1, x2, y2, w2, h2) ->
	distanceSquared: (x1, y1, xprint2, y2) ->
	frames: (from_frame, to_frame, skip) ->
	getImage: (source) -> love.graphics.newImage source
	getColorHSV: (h, s, v) ->
	getColorHue: (color) ->

	getColorSaturation: (color) ->
	getColorValue: (color) ->

	convertColor: (color) -> { lp.getRed(color), lp.getGreen(color), lp.getBlue(color) }
	getColorRGB: (r, g, b) -> r * 0x10000 + g * 0x100 + b
	getRed: (color) -> math.floor color / 0x10000
	getGreen: (color) -> math.floor color / 0x100 % 0x100
	getBlue: (color) -> math.floor color % 0x100

	indexOf: (tbl, item) -> for i, v in ipairs tbl do return i if item == v

	-- read-only
	DEG: -> DEG

	-- read-only
	NUMBER_MAX_VALUE: -> NUMBER_MAX_VALUE

	-- read-only
	RAD: -> RAD

	-- read-only
	VERSION: -> VERSION

	-- read-only
	BASELAYER: -> BASELAYER

	sceneIsNull: => @scene != @__scene or @__scene == nil

	lerp: (a, b, t) ->
	next: (current, options, loop) ->
	prev: (current, options, loop) ->

	rand: (amount) -> math.random(amount)
	randomizeSeed: -> math.randomseed os.time()

	resetCamera: -> camera.x, camera.y = 0, 0

	resize: (width, height) ->
		-- resize scene to scale
		width /= LP.screen\fullScaleX!
		height = height / LP.screen\fullScaleY!
		LP.width, LP.height = width, height
		LP.bounds.width, LP.bounds.height = width, height
		LP.screen\resize!

	rotateAround: (object, anchor, angle, relative) ->
	round: (num, precision) ->
	scale: (value, min, max, min2, max2) ->
	scaleClamp: (value, min, max, min2, max2) ->
	setCamera: (x, y) -> LP.camera.x, LP.camera.y = x, y
	shuffle: (a) ->

	-- Finds the sign of the provided value.
	-- @param	value		The Float to evaluate.
	-- @return	1 if value > 0, -1 if value < 0, and 0 when value == 0.
	sign: (value) -> if value < 0 then -1 else if value == 0 then 0 else 1

	stepTowards: (object, x, y, distance) ->
	swap: (current, a, b) ->
	swapScene: => @__scene = @scene
	timeFlag: ->
	tween: (object, values, duration, options) ->

	-- Used to determine the Löve version that's being used
	__love: (version) -> version == string.format "%d.%d", love._version_major, love._version_minor

LP = lp!

-- Don't allow unknown Löve versions
if not (LP.__love("0.8") or LP.__love("0.9"))
	assert false, "Unsupported Löve version"

require "LunoPunk.geometry.Point"
require "LunoPunk.geometry.Rectangle"
require "LunoPunk.geometry.Matrix"
require "LunoPunk.Scene"
require "LunoPunk.Tweener"

export ^

class lp
	-- Private
	BASELAYER = 10
	DEG = -180 / math.pi
	INT_MAX_VALUE = nil
	NUMBER_MAX_VALUE = nil
	RAD = math.pi / -180
	VERSION = "0.0.1"
	__scene = nil

	_gameTime: nil
	_renderTime: nil
	_systemTime: nil
	_updateTime: nil
	assignedFrameRate: nil
	blackColor: 0x00000000
	bounds: nil
	buffer: nil
	camera: Point!
	console: nil
	defaultFont: "default"
	elapsed: 0
	engine: nil
	entity: nil
	fixed: false
	focused: nil
	frameRate: 0
	log: nil
	matrix: Matrix!
	orientations: nil
	pan: nil
	point: Point!
	point2: Point!
	random: -> math.random!
	randomSeed: nil
	rate: 0
	rect: Rectangle!
	renderMode: nil
	scene: Scene!
	screen: nil
	sprite: nil
	tweener: Tweener!
	volume: nil
	watch: nil
	world: nil
	zero: Point!

	-- Window/resolution related fields
	fullscreen: false
	halfHeight: -> LP.height/2
	halfWidth: -> LP.width/2
	height: 0
	width: 0
	windowHeight: 0
	windowWidth: 0

	alarm: (delay, complete, typeweenType, tweener) ->
	anchorTo: (object, anchor, distance) ->
	angle: (x1, y1, x2, y2) ->
	angleDifference: (angle1, angle2) ->
	angleXY: (object, angle, length, x, y) ->
	approach: (value, target, amount) ->
	choose: (objs) ->
	clamp: (value, min, max) -> if value < min
			min
		else if value > max
			max
		else
			value
	clampInRect: (object, x, y, width, height, padding) ->
	clear: (t) -> while #t > 0 do table.remove t
	colorLerp: (fromColor, toColor, t) ->
	consoleEnabled: ->
	createBitmap: (width, height, transparent, color) ->
	distance: (x1, y1, x2, y2) ->
	istanceRectPoint: (px, py, rx, ry, rw, rh) ->
	distanceRects: (x1, y1, w1, h1, x2, y2, w2, h2) ->
	distanceSquared: (x1, y1, xprint2, y2) ->
	frames: (from_frame, to_frame, skip) ->
	getImage: (source) -> love.graphics.newImage source
	getColorHSV: (h, s, v) ->
	getColorHue: (color) ->

	getColorSaturation: (color) ->
	getColorValue: (color) ->

	convertColor: (color) -> { lp.getRed(color), lp.getGreen(color), lp.getBlue(color) }
	getColorRGB: (r, g, b) -> r * 0x10000 + g * 0x100 + b
	getRed: (color) -> math.floor color / 0x10000
	getGreen: (color) -> math.floor color / 0x100 % 0x100
	getBlue: (color) -> math.floor color % 0x100

	indexOf: (tbl, item) -> for i, v in ipairs tbl do return i if item == v

	-- read-only
	DEG: -> DEG

	-- read-only
	NUMBER_MAX_VALUE: -> NUMBER_MAX_VALUE

	-- read-only
	RAD: -> RAD

	-- read-only
	VERSION: -> VERSION

	-- read-only
	BASELAYER: -> BASELAYER

	sceneIsNull: => @scene != @__scene or @__scene == nil

	lerp: (a, b, t) ->
	next: (current, options, loop) ->
	prev: (current, options, loop) ->

	rand: (amount) -> math.random(amount)
	randomizeSeed: -> math.randomseed os.time()

	resetCamera: -> camera.x, camera.y = 0, 0

	resize: (width, height) ->
		-- resize scene to scale
		width /= LP.screen\fullScaleX!
		height = height / LP.screen\fullScaleY!
		LP.width, LP.height = width, height
		LP.bounds.width, LP.bounds.height = width, height
		LP.screen\resize!

	rotateAround: (object, anchor, angle, relative) ->
	round: (num, precision) ->
	scale: (value, min, max, min2, max2) ->
	scaleClamp: (value, min, max, min2, max2) ->
	setCamera: (x, y) -> LP.camera.x, LP.camera.y = x, y
	shuffle: (a) ->

	-- Finds the sign of the provided value.
	-- @param	value		The Float to evaluate.
	-- @return	1 if value > 0, -1 if value < 0, and 0 when value == 0.
	sign: (value) -> if value < 0 then -1 else if value == 0 then 0 else 1

	stepTowards: (object, x, y, distance) ->
	swap: (current, a, b) ->
	swapScene: => @__scene = @scene
	timeFlag: ->
	tween: (object, values, duration, options) ->

	-- Used to determine the Löve version that's being used
	__love: (version) -> version == string.format "%d.%d", love._version_major, love._version_minor

LP = lp!

-- Don't allow unknown Löve versions
if not (LP.__love("0.8") or LP.__love("0.9"))
	assert false, "Unsupported Löve version"


list: prettier output

list: prettier output

Former-commit-id: 1a9b0fec7bf3c7fde8dd6e615de7edd460ffb455 [formerly 02efb224b3c6879a71079a7580a3a4ffff180794]
Former-commit-id: 00324a02a8f552b3cbac5a55422085b0f1989589

listModules = (source, destination, arg) =>
  out = {}
  for moduleName, moduleTable in next, @Events!['PRIVMSG']
    unless @IsModuleDisabled moduleName, destination
      table.insert out, moduleName
  if #out > 0
    say "Modules: %s", table.concat(out, ' ')

listPatterns = (source, destination, moduleName) =>
  moduleTable = @Events!['PRIVMSG'][moduleName]
  unless moduleTable
    return

  if @IsModuleDisabled moduleName, destination
    return

  out = {}
  for pattern, callback in next, moduleTable
    patt = @ChannelCommandPattern(pattern, moduleName, destination)
    -- Make them prettier
    patt = patt\gsub('%^%%p', '!')
    patt = patt\gsub('%$$', '')
    patt = patt\gsub('^%^', '')
    table.insert out, patt

  if #out > 0
    say "%s patterns:\n %s", moduleName, table.concat(out, ',\n ')

PRIVMSG:
  '^%plist$': listModules
  '^%plist (%w+)$': listPatterns
  '^%phelp$': listModules
  '^%phelp (%w+)$': listPatterns

listModules = (source, destination, arg) =>
  out = {}
  for moduleName, moduleTable in next, @Events!['PRIVMSG']
    unless @IsModuleDisabled moduleName, destination
      table.insert out, moduleName
  if #out > 0
    say "Modules: %s", table.concat(out, ' ')

listPatterns = (source, destination, moduleName) =>
  moduleTable = @Events!['PRIVMSG'][moduleName]
  unless moduleTable
    return

  if @IsModuleDisabled moduleName, destination
    return

  out = {}
  for pattern, callback in next, moduleTable
    patt = @ChannelCommandPattern(pattern, moduleName, destination)
    -- Make them prettier
    patt = patt\gsub('%^%%p', '!')
    patt = patt\gsub('%$$', '')
    patt = patt\gsub('^%^', '')
    table.insert out, patt

  if #out > 0
    say "%s patterns: %s", moduleName, table.concat(out, ', ')

PRIVMSG:
  '^%plist$': listModules
  '^%plist (%w+)$': listPatterns
  '^%phelp$': listModules
  '^%phelp (%w+)$': listPatterns


utf8: use new utf8 utils for less bugs

utf8: use new utf8 utils for less bugs

Former-commit-id: 4e00e8d663f9d7596f91884e5c5c2329f338105f [formerly b846fc9ad5589527247b25d84c136662840747a7]
Former-commit-id: b9d49e142046180dfae8709e81257d6530d77977

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

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

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

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

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

    table.concat(t, "")

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

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

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

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

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

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

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

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

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

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

    table.concat(t, "")

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

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

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

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

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


wording

wording

class Redeem extends require "views.base"
  content: =>
    @render_errors()
    div ->
      if @user
        p "Hello #{@user\get_fullname()}, choose your password:"
        form method: "POST", action: @url_for("auth.redeem", tk: @tk), ->
          label for: "password", ->
            text "Password"
            span class:"form-tip", "At least 5 characters. We use bcrypt."
          input type: "password", id: "password", name: "password"
          label for: "confirm", "Confirm Password"
          input type: "password", id: "confirm", name: "confirm"
          input type: "submit", value: "submit"

class Redeem extends require "views.base"
  content: =>
    @render_errors()
    div ->
      if @user
        p "Hello #{@user\get_fullname()}, choose your password:"
        form method: "POST", action: @url_for("auth.redeem", tk: @tk), ->
          label for: "password", ->
            text "Password"
            span class:"form-tip", "At least 5 characters. We use bcrypt."
          input type: "password", id: "password", name: "password"
          label for: "confirm", "Confirm Password"
          input type: "password", id: "confirm", name: "confirm"
          input type: "submit", value: "signup"


youtube: also delete last id when unsubscribing

Former-commit-id: 0663078f812aaf8ffd4f016a54ff97d6658b05b0 [formerly fa7e4d9594ab403fd6ed4260288a795fa4cc2ab3]
Former-commit-id: b06d23e86209b26bc564f3c378423d3bcf1f96f1

-- Module for announcing new videos to a youtube channel
-- Requires a developer API key set in configuration
{:simplehttp, :json, :urlEncode, :bold} = require'util'

moduleName = 'youtube'
key = moduleName
store = ivar2.persist

getItems = (channel, cb) ->
  url = "https://www.googleapis.com/youtube/v3/channels?part=contentDetails&forUsername=%s&key=%s"
  simplehttp url\format(urlEncode(channel), ivar2.config.youtubeAPIKey), (data) ->
    js = json.decode(data)
    if not (js and js.items and js.items[1] and js.items[1].contentDetails)
      ivar2\Log 'info', 'youtube: No items for youtube channel '..tostring(channel)
      return
    playlistId = js.items[1].contentDetails.relatedPlaylists.uploads
    if playlistId
      url = "https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&playlistId=%s&key=%s"
      simplehttp url\format(playlistId, ivar2.config.youtubeAPIKey), (pldata) ->
        pljs = json.decode(pldata)
        cb(pljs.items)

getLatest = (source, destination, channel) =>
  getItems channel, (items) ->
    for item in *items
      if item.kind == "youtube#playlistItem"
        s = item.snippet
        videoId = s.resourceId.videoId
        say "[#{bold s.channelTitle}] http://youtu.be/#{videoId} #{s.title}"
        return

checkChannels = ->
  for c,_ in pairs(ivar2.channels)
    channelKey = key..':'..c
    channels = store[channelKey] or {}
    for name, channel in pairs(channels)
      lastKey = channelKey .. ':' .. name .. ':last'
      last = store[lastKey] or ''
      getItems name, (items) ->
        for item in *items
          if item.kind == "youtube#playlistItem"
            s = item.snippet
            videoId = s.resourceId.videoId
            -- Check if we already announced
            if videoId == last
              return
            -- Store first video in playlist as a marker
            if s.position == 0
              store[lastKey] = videoId
            ivar2\Msg 'privmsg', channel.channel, nil, "[#{bold s.channelTitle}] http://youtu.be/#{videoId} — #{s.title}"
            -- If we're newly subscribed, only tell once
            if last == ''
              return

subscribe = (source, destination, name) =>
  channelKey = key..':'..destination
  channels = store[channelKey] or {}
  channels[name] = {channel:destination, name:name}
  store[channelKey] = channels
  reply "Ok. Subscribed to #{bold name}"
  checkChannels()

unsubscribe = (source, destination, name) =>
  channelKey = key..':'..destination
  channels = store[channelKey] or {}
  unless channels[name]
    reply "Wasn't subscribed. But, sure."
  else
    channels[name] = nil
    store[channelKey] = channels
    lastKey = channelKey .. ':' .. name .. ':last'
    store[lastKey] = nil
    reply "Ok. Stopped caring about #{bold name}"

list = (source, destination) =>
  channelKey = key..':'..destination
  channels = store[channelKey] or {}
  out = {}
  for name, game in pairs(channels)
      out[#out+1] = name
  say "Subscribed to: #{table.concat(out, ', ')}"

-- Start the subscribe poller
ivar2\Timer('youtube', 300, 300, checkChannels)

PRIVMSG:
  '^%pyoutube latest (.*)': getLatest
  '^%pyoutube subscribe (.*)': subscribe
  '^%pyoutube unsubscribe (.*)': unsubscribe
  '^%pyoutube list': list

-- Module for announcing new videos to a youtube channel
-- Requires a developer API key set in configuration
{:simplehttp, :json, :urlEncode, :bold} = require'util'

moduleName = 'youtube'
key = moduleName
store = ivar2.persist

getItems = (channel, cb) ->
  url = "https://www.googleapis.com/youtube/v3/channels?part=contentDetails&forUsername=%s&key=%s"
  simplehttp url\format(urlEncode(channel), ivar2.config.youtubeAPIKey), (data) ->
    js = json.decode(data)
    if not (js and js.items and js.items[1] and js.items[1].contentDetails)
      ivar2\Log 'info', 'youtube: No items for youtube channel '..tostring(channel)
      return
    playlistId = js.items[1].contentDetails.relatedPlaylists.uploads
    if playlistId
      url = "https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&playlistId=%s&key=%s"
      simplehttp url\format(playlistId, ivar2.config.youtubeAPIKey), (pldata) ->
        pljs = json.decode(pldata)
        cb(pljs.items)

getLatest = (source, destination, channel) =>
  getItems channel, (items) ->
    for item in *items
      if item.kind == "youtube#playlistItem"
        s = item.snippet
        videoId = s.resourceId.videoId
        say "[#{bold s.channelTitle}] http://youtu.be/#{videoId} #{s.title}"
        return

checkChannels = ->
  for c,_ in pairs(ivar2.channels)
    channelKey = key..':'..c
    channels = store[channelKey] or {}
    for name, channel in pairs(channels)
      lastKey = channelKey .. ':' .. name .. ':last'
      last = store[lastKey] or ''
      getItems name, (items) ->
        for item in *items
          if item.kind == "youtube#playlistItem"
            s = item.snippet
            videoId = s.resourceId.videoId
            -- Check if we already announced
            if videoId == last
              return
            -- Store first video in playlist as a marker
            if s.position == 0
              store[lastKey] = videoId
            ivar2\Msg 'privmsg', channel.channel, nil, "[#{bold s.channelTitle}] http://youtu.be/#{videoId} — #{s.title}"
            -- If we're newly subscribed, only tell once
            if last == ''
              return

subscribe = (source, destination, name) =>
  channelKey = key..':'..destination
  channels = store[channelKey] or {}
  channels[name] = {channel:destination, name:name}
  store[channelKey] = channels
  reply "Ok. Subscribed to #{bold name}"
  checkChannels()

unsubscribe = (source, destination, name) =>
  channelKey = key..':'..destination
  channels = store[channelKey] or {}
  unless channels[name]
    reply "Wasn't subscribed. But, sure."
  else
    channels[name] = nil
    store[channelKey] = channels
    reply "Ok. Stopped caring about #{bold name}"

list = (source, destination) =>
  channelKey = key..':'..destination
  channels = store[channelKey] or {}
  out = {}
  for name, game in pairs(channels)
      out[#out+1] = name
  say "Subscribed to: #{table.concat(out, ', ')}"

-- Start the subscribe poller
ivar2\Timer('youtube', 300, 300, checkChannels)

PRIVMSG:
  '^%pyoutube latest (.*)': getLatest
  '^%pyoutube subscribe (.*)': subscribe
  '^%pyoutube unsubscribe (.*)': unsubscribe
  '^%pyoutube list': list


Add löve mappings to utils.Key

Add löve mappings to utils.Key

export ^

class key
	ANY: ""

	LEFT: "left"
	UP: "up"
	RIGHT: "right"
	DOWN: "down"

	ENTER: "return"
	COMMAND: {"lgui", "rgui"}
	CONTROL: {"lctrl", "rctrl"}
	ALT: {"lalt", "ralt"}
	SPACE: " "
	SHIFT: {"lshift", "rshift"}
	BACKSPACE: "backspace"
	CAPS_LOCK: "capslock"
	DELETE: "delete"
	END: "end"
	ESCAPE: "escape"
	HOME: "home"
	INSERT: "insert"
	TAB: "tab"
	PAGE_DOWN: "pagedown"
	PAGE_UP: "pageup"
	LEFT_SQUARE_BRACKET: "["
	RIGHT_SQUARE_BRACKET: "]"
	TILDE: "~"

	A: "a"
	B: "b"
	C: "c"
	D: "d"
	E: "e"
	F: "f"
	G: "g"
	H: "h"
	I: "i"
	J: "j"
	K: "k"
	L: "l"
	M: "m"
	N: "n"
	O: "o"
	P: "p"
	Q: "q"
	R: "r"
	S: "s"
	T: "t"
	U: "u"
	V: "v"
	W: "w"
	X: "x"
	Y: "y"
	Z: "z"

	F1: "f1"
	F2: "f2"
	F3: "f3"
	F4: "f4"
	F5: "f5"
	F6: "f6"
	F7: "f7"
	F8: "f8"
	F9: "f9"
	F10: "f10"
	F11: "f11"
	F12: "f12"
	F13: "f13"
	F14: "f14"
	F15: "f15"

	DIGIT_0: "0"
	DIGIT_1: "1"
	DIGIT_2: "2"
	DIGIT_3: "3"
	DIGIT_4: "4"
	DIGIT_5: "5"
	DIGIT_6: "6"
	DIGIT_7: "7"
	DIGIT_8: "8"
	DIGIT_9: "9"

	NUMPAD_0: "kp0"
	NUMPAD_1: "kp1"
	NUMPAD_2: "kp2"
	NUMPAD_3: "kp3"
	NUMPAD_4: "kp4"
	NUMPAD_5: "kp5"
	NUMPAD_6: "kp6"
	NUMPAD_7: "kp7"
	NUMPAD_8: "kp8"
	NUMPAD_9: "kp9"
	NUMPAD_ADD: "kp+"
	NUMPAD_DECIMAL: "kp."
	NUMPAD_DIVIDE: "kp/"
	NUMPAD_ENTER: "kpenter"
	NUMPAD_MULTIPLY: "kp*"
	NUMPAD_SUBTRACT: "kp-"

Key = key!


Fixed random legs dissapear

Fixed random legs dissapear

--
-- Copyright (C) 2017 DBot
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--

ENABLE_FLASHLIGHT_PASS = CreateConVar('ppm2_flashlight_pass', '1', {FCVAR_ARCHIVE}, 'Enable flashlight render pass. This kills FPS.')
ENABLE_LEGS = CreateConVar('ppm2_draw_legs', '1', {FCVAR_ARCHIVE}, 'Draw pony legs.')
USE_RENDER_OVERRIDE = CreateConVar('ppm2_legs_new', '1', {FCVAR_ARCHIVE}, 'Use RenderOverride function for legs drawing')
LEGS_RENDER_TYPE = CreateConVar('ppm2_render_legstype', '0', {FCVAR_ARCHIVE, FCVAR_NOTIFY}, 'When render legs. 0 - Before Opaque renderables; 1 - after Translucent renderables')

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

    CompileTextures: => @GetTextureController()\CompileTextures() if @GetTextureController and @GetTextureController()
    new: (data) =>
        @objID = @@NEXT_OBJ_ID
        @@NEXT_OBJ_ID += 1
        @isValid = true
        @networkedData = data
        @ent = data.ent
        @hideModels = false
        @modelCached = data\GetModel()
        @IGNORE_DRAW = false
        @CompileTextures() if @ent
        @CreateLegs() if @ent == LocalPlayer()
        @socksModel = data\GetSocksModel()
        @socksModel\SetNoDraw(false) if IsValid(@socksModel)
        @CreateFlexController() if @ent
    __tostring: => "[#{@@__name}:#{@objID}|#{@GetData()}]"
    GetEntity: => @ent
    GetData: => @networkedData
    GetModel: => @networkedData\GetModel()

    GetLegs: =>
        return NULL if not @isValid
        return NULL if @ent ~= LocalPlayer()
        @CreateLegs() if not IsValid()
        return @legsModel
    CreateLegs: =>
        return NULL if not @isValid
        return NULL if @ent ~= LocalPlayer()
        for ent in *ents.GetAll()
            if ent.isPonyLegsModel
                ent\Remove()
        @legsModel = ClientsideModel(@modelCached)
        with @legsModel
            .isPonyLegsModel = true
            .lastRedrawFix = 0
            \SetNoDraw(true)
            .__PPM2_PonyData = @GetData()

        @GetData()\GetWeightController()\UpdateWeight(@legsModel)

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

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

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

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

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

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

        if ply\InVehicle()
            local bonePos

            if bone = @legsModel\LookupBone('LrigNeck1')
                if boneData = @legsModel\GetBonePosition(bone)
                    bonePos = boneData

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

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

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

            drawPos = Vector(@@LEG_SHIFT_CONST_VEHICLE, 0, @@LEG_Z_CONST_VEHICLE)
            drawPos\Rotate(vehAng)
            @legsModel\SetPos(eyepos - drawPos)
            @legsModel\SetRenderOrigin(eyepos - drawPos)

            if not bonePos
                legClipPlanePos = Vector(0, 0, @@LEG_CLIP_OFFSET_VEHICLE)
                legClipPlanePos\Rotate(vehAng)
                @legClipPlanePos = eyepos - legClipPlanePos
            else
                @legClipPlanePos = bonePos

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

            @legsModel\SetRenderAngles(newAng)
            @legsModel\SetAngles(newAng)
            @legsModel\SetRenderOrigin(newPos)
            @legsModel\SetPos(newPos)

            if bone = @legsModel\LookupBone('LrigNeck1')
                if boneData = @legsModel\GetBonePosition(bone)
                    @legClipPlanePos = boneData
                else
                    @legClipPlanePos = Vector(x, y, z + @duckOffsetHack)
            else
                @legClipPlanePos = Vector(x, y, z + @duckOffsetHack)

        @legClipDot = @legsClipPlane\Dot(@legClipPlanePos)

    @LEG_CLIP_VECTOR = Vector(0, 0, -1)
    @LEGS_MAX_DISTANCE = 60 ^ 2
    DrawLegs: (start3D = false) =>
        return if not @isValid
        return if not ENABLE_LEGS\GetBool()
        @CreateLegs() unless IsValid(@legsModel)
        return unless IsValid(@legsModel)
        return if @ent\ShouldDrawLocalPlayer()
        return if (@ent\GetPos() + @ent\GetViewOffset())\DistToSqr(EyePos()) > @@LEGS_MAX_DISTANCE
        if USE_RENDER_OVERRIDE\GetBool()
            @legsModel\SetNoDraw(false)
            rTime = RealTime()
            if @legsModel.lastRedrawFix < rTime
                @legsModel\DrawModel()
                @legsModel.lastRedrawFix = rTime + 5
            if not @legsModel.RenderOverride
                @legsModel.RenderOverride = -> @DrawLegsOverride()
                @legsModel\DrawModel()
            return
        else
            @legsModel\SetNoDraw(true)
        @UpdateLegs()

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

        @GetTextureController()\PreDrawLegs(@legsModel)
        if sizes = @GetData()\GetSizeController()
            sizes\ModifyNeck(@legsModel)
            sizes\ModifyLegs(@legsModel)
            sizes\ModifyScale(@legsModel)
        @legsModel\DrawModel()
        @GetTextureController()\PostDrawLegs(@legsModel)

        if LEGS_RENDER_TYPE\GetBool() and NABLE_FLASHLIGHT_PASS\GetBool()
            render.PushFlashlightMode(true)
            @GetTextureController()\PreDrawLegs(@legsModel)
            if sizes = @GetData()\GetSizeController()
                sizes\ModifyNeck(@legsModel)
                sizes\ModifyLegs(@legsModel)
                sizes\ModifyScale(@legsModel)
            @legsModel\DrawModel()
            @GetTextureController()\PostDrawLegs(@legsModel)
            render.PopFlashlightMode()

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

    DrawLegsOverride: =>
        return if not @isValid
        return if not ENABLE_LEGS\GetBool()
        return if @ent\ShouldDrawLocalPlayer()
        return if (@ent\GetPos() + @ent\GetViewOffset())\DistToSqr(EyePos()) > @@LEGS_MAX_DISTANCE
        @UpdateLegs()

        oldClip = render.EnableClipping(true)
        render.PushCustomClipPlane(@legsClipPlane, @legClipDot)

        @GetTextureController()\PreDrawLegs(@legsModel)
        if sizes = @GetData()\GetSizeController()
            sizes\ModifyNeck(@legsModel)
            sizes\ModifyLegs(@legsModel)
            sizes\ModifyScale(@legsModel)
        @legsModel\DrawModel()
        @GetTextureController()\PostDrawLegs(@legsModel)

        render.PopCustomClipPlane()
        render.EnableClipping(oldClip)

    DrawLegsDepth: (start3D = false) =>
        return if not @isValid
        return if not ENABLE_LEGS\GetBool()
        @CreateLegs() unless IsValid(@legsModel)
        return unless IsValid(@legsModel)
        return if @ent\ShouldDrawLocalPlayer()
        return if (@ent\GetPos() + @ent\GetViewOffset())\DistToSqr(EyePos()) > @@LEGS_MAX_DISTANCE
        @UpdateLegs()

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

        @GetTextureController()\PreDrawLegs(@legsModel)
        if sizes = @GetData()\GetSizeController()
            sizes\ModifyNeck(@legsModel)
            sizes\ModifyLegs(@legsModel)
            sizes\ModifyScale(@legsModel)
        @legsModel\DrawModel()
        @GetTextureController()\PostDrawLegs()

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

    IsValid: => IsValid(@ent) and @isValid
    Reset: =>
        @flexes\Reset() if @flexes and @flexes.Reset
        @GetTextureController()\Reset() if @GetTextureController and @GetTextureController() and @GetTextureController().Reset
        @GetTextureController()\ResetTextures() if @GetTextureController and @GetTextureController()
    Remove: =>
        @flexes\Remove() if @flexes
        @GetTextureController()\Remove() if @GetTextureController and @GetTextureController()
        @isValid = false

    PlayerDeath: =>
        return if not @isValid
        @HideModels(true)
        @GetTextureController()\ResetTextures() if @GetTextureController() and @ent\IsPony()

    PlayerRespawn: =>
        return if not @isValid
        @HideModels(false) if @ent\IsPony()
        @flexes\PlayerRespawn() if @flexes
        @GetTextureController()\ResetTextures() if @GetTextureController()

    DrawModels: =>
        @socksModel\DrawModel() if IsValid(@socksModel)
    HideModels: (status = true) =>
        return if @hideModels == status
        @socksModel\SetNoDraw(status) if IsValid(@socksModel)
        @hideModels = status

    PreDraw: (ent = @ent) =>
        return if not @isValid
        @GetTextureController()\PreDraw(ent)
        @GetTextureController()\UpdateSocks(@ent, @socksModel) if IsValid(@socksModel) and PPM2.ALTERNATIVE_RENDER\GetBool()
        @flexes\Think(ent) if @flexes
    PostDraw: (ent = @ent) =>
        return if not @isValid
        @GetTextureController()\PostDraw(ent)

    @ARMS_MATERIAL_INDEX = 0
    PreDrawArms: (ent) =>
        return if not @isValid
        if ent and not @armsWeightSetup
            @armsWeightSetup = true
            weight = @GetData()\GetWeight()
            vec = Vector(weight, weight, weight)
            for i = 1, 13
                ent\ManipulateBoneScale(i, vec)
        render.MaterialOverrideByIndex(@@ARMS_MATERIAL_INDEX, @GetTextureController()\GetBody())
    PostDrawArms: (ent) =>
        return if not @isValid
        render.MaterialOverrideByIndex(@@ARMS_MATERIAL_INDEX)

    DataChanges: (state) =>
        return if not @isValid
        return if not @ent
        @GetTextureController()\DataChanges(state)
        @flexes\DataChanges(state) if @flexes
        switch state\GetKey()
            when 'Weight'
                @armsWeightSetup = false
                @GetData()\GetWeightController()\UpdateWeight(@legsModel) if IsValid(@legsModel)
            when 'SocksModel'
                @socksModel = @GetData()\GetSocksModel()
                @socksModel\SetNoDraw(@hideModels) if IsValid(@socksModel)
                @GetTextureController()\UpdateSocks(@ent, @socksModel) if @GetTextureController() and IsValid(@socksModel)
            when 'NoFlex'
                if state\GetValue()
                    @flexes\ResetSequences() if @flexes
                    @flexes = nil
                else
                    @CreateFlexController()
    GetTextureController: =>
        return @renderController if not @isValid
        if not @renderController
            cls = PPM2.GetTextureController(@modelCached)
            @renderController = cls(@)
        @renderController.ent = @ent
        return @renderController
    CreateFlexController: =>
        return @flexes if not @isValid
        return if @GetData()\GetNoFlex()
        if not @flexes
            cls = PPM2.GetFlexController(@modelCached)
            return if not cls
            @flexes = cls(@)
        @flexes.ent = @ent
        return @flexes
    GetFlexController: => @flexes

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

    new: (data) =>
        super(data)
        @upperManeModel = data\GetUpperManeModel()
        @lowerManeModel = data\GetLowerManeModel()
        @tailModel = data\GetTailModel()
        @upperManeModel\SetNoDraw(@hideModels) if IsValid(@upperManeModel)
        @lowerManeModel\SetNoDraw(@hideModels) if IsValid(@lowerManeModel)
        @tailModel\SetNoDraw(@hideModels) if IsValid(@tailModel)
    __tostring: => "[#{@@__name}:#{@objID}|#{@GetData()}]"

    DataChanges: (state) =>
        return if not @ent
        return if not @isValid
        switch state\GetKey()
            when 'UpperManeModel'
                @upperManeModel = @GetData()\GetUpperManeModel()
                @upperManeModel\SetNoDraw(@hideModels) if IsValid(@upperManeModel)
                @GetTextureController()\UpdateUpperMane(@ent, @upperManeModel) if @GetTextureController() and IsValid(@upperManeModel)
            when 'LowerManeModel'
                @lowerManeModel = @GetData()\GetLowerManeModel()
                @lowerManeModel\SetNoDraw(@hideModels) if IsValid(@lowerManeModel)
                @GetTextureController()\UpdateLowerMane(@ent, @lowerManeModel) if @GetTextureController() and IsValid(@lowerManeModel)
            when 'TailModel'
                @tailModel = @GetData()\GetTailModel()
                @tailModel\SetNoDraw(@hideModels) if IsValid(@tailModel)
                @GetTextureController()\UpdateTail(@ent, @tailModel) if @GetTextureController() and IsValid(@tailModel)
        super(state)

    DrawModels: =>
        @upperManeModel\DrawModel() if IsValid(@upperManeModel)
        @lowerManeModel\DrawModel() if IsValid(@lowerManeModel)
        @tailModel\DrawModel() if IsValid(@tailModel)
        super()
    HideModels: (status = true) =>
        return if @hideModels == status
        @upperManeModel\SetNoDraw(status) if IsValid(@upperManeModel)
        @lowerManeModel\SetNoDraw(status) if IsValid(@lowerManeModel)
        @tailModel\SetNoDraw(status) if IsValid(@tailModel)
        super(status)

    PreDraw: (ent = @ent) =>
        super(ent)
        if PPM2.ALTERNATIVE_RENDER\GetBool()
            textures = @GetTextureController()
            return if not textures
            textures\UpdateUpperMane(@ent, @upperManeModel) if IsValid(@upperManeModel)
            textures\UpdateLowerMane(@ent, @lowerManeModel) if IsValid(@lowerManeModel)
            textures\UpdateTail(@ent, @tailModel) if IsValid(@tailModel)

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

--
-- Copyright (C) 2017 DBot
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--

ENABLE_FLASHLIGHT_PASS = CreateConVar('ppm2_flashlight_pass', '1', {FCVAR_ARCHIVE}, 'Enable flashlight render pass. This kills FPS.')
ENABLE_LEGS = CreateConVar('ppm2_draw_legs', '1', {FCVAR_ARCHIVE}, 'Draw pony legs.')
USE_RENDER_OVERRIDE = CreateConVar('ppm2_legs_new', '1', {FCVAR_ARCHIVE}, 'Use RenderOverride function for legs drawing')
LEGS_RENDER_TYPE = CreateConVar('ppm2_render_legstype', '0', {FCVAR_ARCHIVE, FCVAR_NOTIFY}, 'When render legs. 0 - Before Opaque renderables; 1 - after Translucent renderables')

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

    CompileTextures: => @GetTextureController()\CompileTextures() if @GetTextureController and @GetTextureController()
    new: (data) =>
        @objID = @@NEXT_OBJ_ID
        @@NEXT_OBJ_ID += 1
        @isValid = true
        @networkedData = data
        @ent = data.ent
        @hideModels = false
        @modelCached = data\GetModel()
        @IGNORE_DRAW = false
        @CompileTextures() if @ent
        @CreateLegs() if @ent == LocalPlayer()
        @socksModel = data\GetSocksModel()
        @socksModel\SetNoDraw(false) if IsValid(@socksModel)
        @CreateFlexController() if @ent
    __tostring: => "[#{@@__name}:#{@objID}|#{@GetData()}]"
    GetEntity: => @ent
    GetData: => @networkedData
    GetModel: => @networkedData\GetModel()

    GetLegs: =>
        return NULL if not @isValid
        return NULL if @ent ~= LocalPlayer()
        @CreateLegs() if not IsValid()
        return @legsModel
    CreateLegs: =>
        return NULL if not @isValid
        return NULL if @ent ~= LocalPlayer()
        for ent in *ents.GetAll()
            if ent.isPonyLegsModel
                ent\Remove()
        @legsModel = ClientsideModel(@modelCached)
        with @legsModel
            .isPonyLegsModel = true
            \SetNoDraw(true)
            .__PPM2_PonyData = @GetData()

        @GetData()\GetWeightController()\UpdateWeight(@legsModel)

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

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

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

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

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

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

        if ply\InVehicle()
            local bonePos

            if bone = @legsModel\LookupBone('LrigNeck1')
                if boneData = @legsModel\GetBonePosition(bone)
                    bonePos = boneData

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

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

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

            drawPos = Vector(@@LEG_SHIFT_CONST_VEHICLE, 0, @@LEG_Z_CONST_VEHICLE)
            drawPos\Rotate(vehAng)
            @legsModel\SetPos(eyepos - drawPos)
            @legsModel\SetRenderOrigin(eyepos - drawPos)

            if not bonePos
                legClipPlanePos = Vector(0, 0, @@LEG_CLIP_OFFSET_VEHICLE)
                legClipPlanePos\Rotate(vehAng)
                @legClipPlanePos = eyepos - legClipPlanePos
            else
                @legClipPlanePos = bonePos

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

            @legsModel\SetRenderAngles(newAng)
            @legsModel\SetAngles(newAng)
            @legsModel\SetRenderOrigin(newPos)
            @legsModel\SetPos(newPos)

            if bone = @legsModel\LookupBone('LrigNeck1')
                if boneData = @legsModel\GetBonePosition(bone)
                    @legClipPlanePos = boneData
                else
                    @legClipPlanePos = Vector(x, y, z + @duckOffsetHack)
            else
                @legClipPlanePos = Vector(x, y, z + @duckOffsetHack)

        @legClipDot = @legsClipPlane\Dot(@legClipPlanePos)

    @LEG_CLIP_VECTOR = Vector(0, 0, -1)
    @LEGS_MAX_DISTANCE = 60 ^ 2
    DrawLegs: (start3D = false) =>
        return if not @isValid
        return if not ENABLE_LEGS\GetBool()
        @CreateLegs() unless IsValid(@legsModel)
        return unless IsValid(@legsModel)
        return if @ent\ShouldDrawLocalPlayer()
        return if (@ent\GetPos() + @ent\GetViewOffset())\DistToSqr(EyePos()) > @@LEGS_MAX_DISTANCE
        if USE_RENDER_OVERRIDE\GetBool()
            @legsModel\SetNoDraw(false)
            if not @legsModel.RenderOverride
                @legsModel.RenderOverride = -> @DrawLegsOverride()
                @legsModel\DrawModel()
            return
        else
            @legsModel\SetNoDraw(true)
        @UpdateLegs()

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

        @GetTextureController()\PreDrawLegs(@legsModel)
        if sizes = @GetData()\GetSizeController()
            sizes\ModifyNeck(@legsModel)
            sizes\ModifyLegs(@legsModel)
            sizes\ModifyScale(@legsModel)
        @legsModel\DrawModel()
        @GetTextureController()\PostDrawLegs(@legsModel)

        if LEGS_RENDER_TYPE\GetBool() and NABLE_FLASHLIGHT_PASS\GetBool()
            render.PushFlashlightMode(true)
            @GetTextureController()\PreDrawLegs(@legsModel)
            if sizes = @GetData()\GetSizeController()
                sizes\ModifyNeck(@legsModel)
                sizes\ModifyLegs(@legsModel)
                sizes\ModifyScale(@legsModel)
            @legsModel\DrawModel()
            @GetTextureController()\PostDrawLegs(@legsModel)
            render.PopFlashlightMode()

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

    DrawLegsOverride: =>
        return if not @isValid
        return if not ENABLE_LEGS\GetBool()
        return if @ent\ShouldDrawLocalPlayer()
        return if (@ent\GetPos() + @ent\GetViewOffset())\DistToSqr(EyePos()) > @@LEGS_MAX_DISTANCE
        @UpdateLegs()

        oldClip = render.EnableClipping(true)
        render.PushCustomClipPlane(@legsClipPlane, @legClipDot)

        @GetTextureController()\PreDrawLegs(@legsModel)
        if sizes = @GetData()\GetSizeController()
            sizes\ModifyNeck(@legsModel)
            sizes\ModifyLegs(@legsModel)
            sizes\ModifyScale(@legsModel)
        @legsModel\DrawModel()
        @GetTextureController()\PostDrawLegs(@legsModel)

        render.PopCustomClipPlane()
        render.EnableClipping(oldClip)

    DrawLegsDepth: (start3D = false) =>
        return if not @isValid
        return if not ENABLE_LEGS\GetBool()
        @CreateLegs() unless IsValid(@legsModel)
        return unless IsValid(@legsModel)
        return if @ent\ShouldDrawLocalPlayer()
        return if (@ent\GetPos() + @ent\GetViewOffset())\DistToSqr(EyePos()) > @@LEGS_MAX_DISTANCE
        @UpdateLegs()

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

        @GetTextureController()\PreDrawLegs(@legsModel)
        if sizes = @GetData()\GetSizeController()
            sizes\ModifyNeck(@legsModel)
            sizes\ModifyLegs(@legsModel)
            sizes\ModifyScale(@legsModel)
        @legsModel\DrawModel()
        @GetTextureController()\PostDrawLegs()

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

    IsValid: => IsValid(@ent) and @isValid
    Reset: =>
        @flexes\Reset() if @flexes and @flexes.Reset
        @GetTextureController()\Reset() if @GetTextureController and @GetTextureController() and @GetTextureController().Reset
        @GetTextureController()\ResetTextures() if @GetTextureController and @GetTextureController()
    Remove: =>
        @flexes\Remove() if @flexes
        @GetTextureController()\Remove() if @GetTextureController and @GetTextureController()
        @isValid = false

    PlayerDeath: =>
        return if not @isValid
        @HideModels(true)
        @GetTextureController()\ResetTextures() if @GetTextureController() and @ent\IsPony()

    PlayerRespawn: =>
        return if not @isValid
        @HideModels(false) if @ent\IsPony()
        @flexes\PlayerRespawn() if @flexes
        @GetTextureController()\ResetTextures() if @GetTextureController()

    DrawModels: =>
        @socksModel\DrawModel() if IsValid(@socksModel)
    HideModels: (status = true) =>
        return if @hideModels == status
        @socksModel\SetNoDraw(status) if IsValid(@socksModel)
        @hideModels = status

    PreDraw: (ent = @ent) =>
        return if not @isValid
        @GetTextureController()\PreDraw(ent)
        @GetTextureController()\UpdateSocks(@ent, @socksModel) if IsValid(@socksModel) and PPM2.ALTERNATIVE_RENDER\GetBool()
        @flexes\Think(ent) if @flexes
    PostDraw: (ent = @ent) =>
        return if not @isValid
        @GetTextureController()\PostDraw(ent)

    @ARMS_MATERIAL_INDEX = 0
    PreDrawArms: (ent) =>
        return if not @isValid
        if ent and not @armsWeightSetup
            @armsWeightSetup = true
            weight = @GetData()\GetWeight()
            vec = Vector(weight, weight, weight)
            for i = 1, 13
                ent\ManipulateBoneScale(i, vec)
        render.MaterialOverrideByIndex(@@ARMS_MATERIAL_INDEX, @GetTextureController()\GetBody())
    PostDrawArms: (ent) =>
        return if not @isValid
        render.MaterialOverrideByIndex(@@ARMS_MATERIAL_INDEX)

    DataChanges: (state) =>
        return if not @isValid
        return if not @ent
        @GetTextureController()\DataChanges(state)
        @flexes\DataChanges(state) if @flexes
        switch state\GetKey()
            when 'Weight'
                @armsWeightSetup = false
                @GetData()\GetWeightController()\UpdateWeight(@legsModel) if IsValid(@legsModel)
            when 'SocksModel'
                @socksModel = @GetData()\GetSocksModel()
                @socksModel\SetNoDraw(@hideModels) if IsValid(@socksModel)
                @GetTextureController()\UpdateSocks(@ent, @socksModel) if @GetTextureController() and IsValid(@socksModel)
            when 'NoFlex'
                if state\GetValue()
                    @flexes\ResetSequences() if @flexes
                    @flexes = nil
                else
                    @CreateFlexController()
    GetTextureController: =>
        return @renderController if not @isValid
        if not @renderController
            cls = PPM2.GetTextureController(@modelCached)
            @renderController = cls(@)
        @renderController.ent = @ent
        return @renderController
    CreateFlexController: =>
        return @flexes if not @isValid
        return if @GetData()\GetNoFlex()
        if not @flexes
            cls = PPM2.GetFlexController(@modelCached)
            return if not cls
            @flexes = cls(@)
        @flexes.ent = @ent
        return @flexes
    GetFlexController: => @flexes

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

    new: (data) =>
        super(data)
        @upperManeModel = data\GetUpperManeModel()
        @lowerManeModel = data\GetLowerManeModel()
        @tailModel = data\GetTailModel()
        @upperManeModel\SetNoDraw(@hideModels) if IsValid(@upperManeModel)
        @lowerManeModel\SetNoDraw(@hideModels) if IsValid(@lowerManeModel)
        @tailModel\SetNoDraw(@hideModels) if IsValid(@tailModel)
    __tostring: => "[#{@@__name}:#{@objID}|#{@GetData()}]"

    DataChanges: (state) =>
        return if not @ent
        return if not @isValid
        switch state\GetKey()
            when 'UpperManeModel'
                @upperManeModel = @GetData()\GetUpperManeModel()
                @upperManeModel\SetNoDraw(@hideModels) if IsValid(@upperManeModel)
                @GetTextureController()\UpdateUpperMane(@ent, @upperManeModel) if @GetTextureController() and IsValid(@upperManeModel)
            when 'LowerManeModel'
                @lowerManeModel = @GetData()\GetLowerManeModel()
                @lowerManeModel\SetNoDraw(@hideModels) if IsValid(@lowerManeModel)
                @GetTextureController()\UpdateLowerMane(@ent, @lowerManeModel) if @GetTextureController() and IsValid(@lowerManeModel)
            when 'TailModel'
                @tailModel = @GetData()\GetTailModel()
                @tailModel\SetNoDraw(@hideModels) if IsValid(@tailModel)
                @GetTextureController()\UpdateTail(@ent, @tailModel) if @GetTextureController() and IsValid(@tailModel)
        super(state)

    DrawModels: =>
        @upperManeModel\DrawModel() if IsValid(@upperManeModel)
        @lowerManeModel\DrawModel() if IsValid(@lowerManeModel)
        @tailModel\DrawModel() if IsValid(@tailModel)
        super()
    HideModels: (status = true) =>
        return if @hideModels == status
        @upperManeModel\SetNoDraw(status) if IsValid(@upperManeModel)
        @lowerManeModel\SetNoDraw(status) if IsValid(@lowerManeModel)
        @tailModel\SetNoDraw(status) if IsValid(@tailModel)
        super(status)

    PreDraw: (ent = @ent) =>
        super(ent)
        if PPM2.ALTERNATIVE_RENDER\GetBool()
            textures = @GetTextureController()
            return if not textures
            textures\UpdateUpperMane(@ent, @upperManeModel) if IsValid(@upperManeModel)
            textures\UpdateLowerMane(@ent, @lowerManeModel) if IsValid(@lowerManeModel)
            textures\UpdateTail(@ent, @tailModel) if IsValid(@tailModel)

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


improved user management enabler

improved user management enabler

Users=require "lazuli.modules.user_management.models.users"
import dump from require "moonscript.util"

=>
  f=io.open "/tmp/a","w"
  f\write dump @
  f\close!
  @@modules or={}
  @include "lazuli.modules.user_management" if not @modules.user_management
  @@modules.user_management or={}
  @before_filter =>
    @session.modules.user_management or={}
    if @session.modules.user_management.currentuser and not @modules.user_management.currentuser
      @modules.user_management.currentuser = Users\find @session.modules.user_management.currentuser

Users=require "lazuli.modules.user_management.models.users"
import dump from require "moonscript.util"

=>
  f=io.open "/tmp/a","w"
  f\write dump @
  f\close!
  @modules or={}
  @include "lazuli.modules.user_management" if not @modules.user_management
  @modules.user_management or={}
  @before_filter =>
    @session.modules.user_management or={}
    if @session.modules.user_management.currentuser and not @modules.user_management.currentuser
      @modules.user_management.currentuser = Users\find @session.modules.user_management.currentuser


Actually making the folders.

Actually making the folders.

rex = require "rex_pcre"
cli = require "cliargs"
lfs = require "lfs"

import insert from table

contains = (t, item) ->
	for k, v in pairs t
		return true if v == item
	false

local dir_list, dir_listing
dir_listing = (path, file) ->
	f = path .. '/' .. file
	attr = lfs.attributes(f).mode
	{file, attr, if attr == 'directory' then dir_list(f)}

dir_list = (path) ->
	[dir_listing(path, file) for file in lfs.dir(path) when not contains({'.', '..'}, file)]

map_listing = (structure, func) ->
	results = {}
	for k, v in pairs structure
		res = func(v, map_listing)
		insert(results, res) if res
	results

print_listing = (value, func) ->
	{file, mode, data} = value
	print file, mode
	func(data, print_listing) if data

file_ext = (str) ->
	"." .. str\match(".([^%.]+)$")

dbg = (b) ->
	"[DBG: #{(b and 'Y') or 'N'}]"

perform_action = (old, new, action) ->
	dbg(debug) .. switch action
		when "copy"
			"[cp]: Not yet implemented."
		when "move"
			"[mv]: Not yet implemented."
		when "mkdir"
			lfs.mkdir(out_f) if not debug
			"[mkdir]: #{old} to: #{new}"
		when "symlink", "hardlink"
			lfs.link(old, new, action == "hardlink" or false) if not debug
			"[link]: #{old} to: #{new}"
		else
			"unknown action: #{action}"

process_files = (filedata, regex, parent) ->
	for k, {file, mode, data} in pairs filedata

		if rex.gsub(file, regex, '')

			isdir = (md) -> if md == 'directory' then true else false

			in_f = parent.in .. '/' .. file
			out_f = if isdir(mode)
				do
					f = file
					->
					f = rex.gsub(f, regex, '')
					f = f\match("^%s*(.-)%s*$")
					parent.out .. '/' .. f
			else
				do
					f = file
					->
					f = rex.gsub(f, regex, '')
					f = rex.match(f, [[(.*)(v\d)]]) or f
					f = f\gsub('[ ]', '')
					f = rex.gsub(f, [[([^\d]*)(\d+)[^\d]*$]], [[%1_ep%2]], 1)
					f ..= file_ext(file)
					parent.out .. '/' .. f

			action = if isdir(mode) then "mkdir" else args["a"]
			print perform_action(in_f, out_f, action)
			process_files(data, regex, {in: in_f, out: out_f}) if data

		else
			print "ERR: ", file, mode

cli\set_name("regexulon.lua")
cli\add_argument("DIR", "directory to scan")
cli\add_argument("TARGET", "target directory")
cli\optarg("IGNORED", "ignored files/directories", "")
cli\add_option("-a, --action=ACTION", "action to take: cp, mv, symlink, hardlink", "hardlink")
cli\add_flag("-e, --existing", "skip creating previously existing target directories.")
cli\add_flag("-d, --debug", "script will simulate execution.")
cli\add_flag("-V, --version", "prints the program version")
cli\add_flag("-v, --verbose", "verbose output")

export args = cli\parse_args()
return if not args

ignored_files = switch type(args["IGNORED"])
	when "string" then {}
	else args["IGNORED"]

inputdir = args["DIR"]
targetdir = args["TARGET"]
export debug = args["d"]

files = dir_list(args["DIR"])
regex = [==[\(.*?\)|\[.*?\]]==]

--map_listing(files, print_listing)
processed = process_files(files, regex, {in: inputdir, out: targetdir})

rex = require "rex_pcre"
cli = require "cliargs"
lfs = require "lfs"

import insert from table

contains = (t, item) ->
	for k, v in pairs t
		return true if v == item
	false

local dir_list, dir_listing
dir_listing = (path, file) ->
	f = path .. '/' .. file
	attr = lfs.attributes(f).mode
	{file, attr, if attr == 'directory' then dir_list(f)}

dir_list = (path) ->
	[dir_listing(path, file) for file in lfs.dir(path) when not contains({'.', '..'}, file)]

map_listing = (structure, func) ->
	results = {}
	for k, v in pairs structure
		res = func(v, map_listing)
		insert(results, res) if res
	results

print_listing = (value, func) ->
	{file, mode, data} = value
	print file, mode
	func(data, print_listing) if data

file_ext = (str) ->
	"." .. str\match(".([^%.]+)$")

dbg = (b) ->
	"[DBG: #{(b and 'Y') or 'N'}]"

perform_action = (old, new, action) ->
	dbg(debug) .. switch action
		when "copy"
			"[cp]: Not yet implemented."
		when "move"
			"[mv]: Not yet implemented."
		when "mkdir"
			"[mkdir]: #{old} to: #{new}"
		when "symlink", "hardlink"
			lfs.link(old, new, action == "hardlink" or false) if not debug
			"[link]: #{old} to: #{new}"
		else
			"unknown action: #{action}"

process_files = (filedata, regex, parent) ->
	for k, {file, mode, data} in pairs filedata
		if rex.gsub(file, regex, '')

			isdir = (md) -> if md == 'directory' then true else false

			in_f = parent.in .. '/' .. file
			out_f = if isdir(mode)
				do
					f = file
					->
					f = rex.gsub(f, regex, '')
					f = f\match("^%s*(.-)%s*$")
					parent.out .. '/' .. f
			else
				do
					f = file
					->
					f = rex.gsub(f, regex, '')
					f = rex.match(f, [[(.*)(v\d)]]) or f
					f = f\gsub('[ ]', '')
					f = rex.gsub(f, [[([^\d]*)(\d+)[^\d]*$]], [[%1_ep%2]], 1)
					f ..= file_ext(file)
					parent.out .. '/' .. f

			action = if isdir(mode) then "mkdir" else args["a"]
			print perform_action(in_f, out_f, action)
			process_files(data, regex, {in: in_f, out: out_f}) if data

		else
			print "ERR: ", file, mode

cli\set_name("regexulon.lua")
cli\add_argument("DIR", "directory to scan")
cli\add_argument("TARGET", "target directory")
cli\optarg("IGNORED", "ignored files/directories", "")
cli\add_option("-a, --action=ACTION", "action to take: cp, mv, symlink, hardlink", "hardlink")
cli\add_flag("-e, --existing", "skip creating previously existing target directories.")
cli\add_flag("-d, --debug", "script will simulate execution.")
cli\add_flag("-V, --version", "prints the program version")
cli\add_flag("-v, --verbose", "verbose output")

export args = cli\parse_args()
return if not args

ignored_files = switch type(args["IGNORED"])
	when "string" then {}
	else args["IGNORED"]

inputdir = args["DIR"]
targetdir = args["TARGET"]
export debug = args["d"]

files = dir_list(args["DIR"])
regex = [==[\(.*?\)|\[.*?\]]==]

--map_listing(files, print_listing)
processed = process_files(files, regex, {in: inputdir, out: targetdir})