printing result was a bad idea.

printing result was a bad idea.

-- moonraker
-- simple build system. so simple you probably shouldn't use it
-- look at example .moonraker
-- to do anything sufficiently useful you'll need luafilesystem
moonscript = require "moonscript"

tasks = {} -- each task
descs = {} -- descriptions of each task
done  = {} -- tasks already executed
multi = {} -- tasks that can be executed multiple times
requires = {} -- each task's required tasks

default   = nil -- default task
nextdesc  = nil -- description to assign to next defined task
nextmulti = nil -- whether the next defined task is able to execute multiple times
tmpname = os.tmpname! -- temporary file for output

-- functions available to moonraker
environment =
  task: (taskt, block) ->
    name = nil
    for k, v in pairs taskt
      name = k -- task name is the only element in the table
      requires[k] = v unless type(v) == "function"
      tasks[k] = v if type(v) == "function"
      descs[k] = nextdesc -- set our description

    tasks[name] = type(block) == "table" and block.do or block if block
    multi[name] = nextmulti if nextmulti
    nextdesc, nextmulti = nil, nil
  desc: (name) ->
    nextdesc = name
  multiple: ->
    nextmulti = true
  default: (name) ->
    print("Already set default task: #{default}") if default
    default = name
  windows: ->
    package.config\sub(1,1) == "\\"

-- transforms unknown access to os execution
setmetatable environment, __index: (t, k) ->
  return _G[k] if _G[k]
  (x) =>
    returncode = os.execute("#{k} #{x or @} > #{tmpname}")
    result = [l for l in io.lines(tmpname)]
    table.concat(result, "\n"), returncode

doTask = (name) ->
  return if not multi[name] and done[name] -- task already executed
  unless tasks[name]
    return print("No task: " .. name)
  task = tasks[name]
  if requires[name]
    if type(requires[name]) == "table"
      for item in *requires[name]
        doTask(item)
    else
      doTask(requires[name])
  start = os.clock()
  safe, res = pcall(task, environment)
  if safe
    done[name] = true
    elapsed = os.clock() - start
    print(("Completed %s in %0.2fs.")\format(name, elapsed))
  else
    print "Error in task: #{name}\n #{res}"

help = "\tUsage: moonraker file (default: .moonraker) task (optional)"

doFile = (file) ->
  start = os.clock()
  filepath = file or arg[2] and arg[1] or ".moonraker"
  if filepath\match("%-h.*") --anything matching -h* must be a request for help
    print help
    return
  mrfile = table.concat([l for l in io.lines(filepath)], "\n")
  --try to load moonraker
  mrfunc = assert(moonscript.loadstring(mrfile))
  setfenv(mrfunc, environment)
  safe, errors = pcall(mrfunc)
  task = arg[1] and arg[2] or arg[1] or default
  if safe
    doTask(task)
  --always remove the temporary file
  os.remove(tmpname)
  -- print errors in moonraker file
  if errors
    print errors
  elapsed = os.clock() - start
  print(("Completed all tasks in %0.2fs.")\format(elapsed))

--public
doFile

-- moonraker
-- simple build system. so simple you probably shouldn't use it
-- look at example .moonraker
-- to do anything sufficiently useful you'll need luafilesystem
moonscript = require "moonscript"

tasks = {} -- each task
descs = {} -- descriptions of each task
done  = {} -- tasks already executed
multi = {} -- tasks that can be executed multiple times
requires = {} -- each task's required tasks

default   = nil -- default task
nextdesc  = nil -- description to assign to next defined task
nextmulti = nil -- whether the next defined task is able to execute multiple times
tmpname = os.tmpname! -- temporary file for output

-- functions available to moonraker
environment =
  task: (taskt, block) ->
    name = nil
    for k, v in pairs taskt
      name = k -- task name is the only element in the table
      requires[k] = v unless type(v) == "function"
      tasks[k] = v if type(v) == "function"
      descs[k] = nextdesc -- set our description

    tasks[name] = type(block) == "table" and block.do or block if block
    multi[name] = nextmulti if nextmulti
    nextdesc, nextmulti = nil, nil
  desc: (name) ->
    nextdesc = name
  multiple: ->
    nextmulti = true
  default: (name) ->
    print("Already set default task: #{default}") if default
    default = name
  windows: ->
    package.config\sub(1,1) == "\\"

-- transforms unknown access to os execution
setmetatable environment, __index: (t, k) ->
  return _G[k] if _G[k]
  (x) =>
    returncode = os.execute("#{k} #{x or @} > #{tmpname}")
    result = [l for l in io.lines(tmpname)]
    table.concat(result, "\n"), returncode

doTask = (name) ->
  return if not multi[name] and done[name] -- task already executed
  unless tasks[name]
    return print("No task: " .. name)
  task = tasks[name]
  if requires[name]
    if type(requires[name]) == "table"
      for item in *requires[name]
        doTask(item)
    else
      doTask(requires[name])
  start = os.clock()
  safe, res = pcall(task, environment)
  if safe
    print res if res
    done[name] = true
    elapsed = os.clock() - start
    print(("Completed %s in %0.2fs.")\format(name, elapsed))
  else
    print "Error in task: #{name}\n #{res}"

help = "\tUsage: moonraker file (default: .moonraker) task (optional)"

doFile = (file) ->
  start = os.clock()
  filepath = file or arg[2] and arg[1] or ".moonraker"
  if filepath\match("%-h.*") --anything matching -h* must be a request for help
    print help
    return
  mrfile = table.concat([l for l in io.lines(filepath)], "\n")
  --try to load moonraker
  mrfunc = assert(moonscript.loadstring(mrfile))
  setfenv(mrfunc, environment)
  safe, errors = pcall(mrfunc)
  task = arg[1] and arg[2] or arg[1] or default
  if safe
    doTask(task)
  --always remove the temporary file
  os.remove(tmpname)
  -- print errors in moonraker file
  if errors
    print errors
  elapsed = os.clock() - start
  print(("Completed all tasks in %0.2fs.")\format(elapsed))

--public
doFile


improved module system

improved module system

lazuli = require "lazuli"
import capture_errors,assert_error from require "lapis.application"
import validate_functions, assert_valid from require "lapis.validate"
import hmac_sha1 from require "lapis.util.encoding"
import encode_base64 from require "lapis.util.encoding"
import cached from require "lapis.cache"
csrf = require "lapis.csrf"
config = (require "lapis.config").get!

Users=require "lazuli.modules.user_management.models.users"

validate_functions.user_doesnt_exist = (username) ->
  u=Users\find username: username
  return not u, "user name already taken"
validate_functions.user_exists = (username) ->
  u=Users\find username: username
  return u and true or false, "user does not exist"

class UsersApplication extends lazuli.Application
  @path: "/users"
  @name: "modules_user_management_"
  @before_filter =>
    @modules.user_management or={}
    @session.modules.user_management or={}
    if not @modules.user_management.providers
      if type(config.modules.user_management)=="table" and type(config.modules.user_management.providers)=="table"
        @modules.user_management.providers={n,require(n)(@,n) for n in *config.modules.user_management.providers}
    @modules.user_management.csrf_token = csrf.generate_token @, "lazuli_modules_usermanagement"
    if @session.modules.user_management.currentuser and not @modules.user_management.currentuser
      @modules.user_management.currentuser = Users\find @session.modules.user_management.currentuser
  [register: "/register"]: cached exptime: 60,[1]:=>
    render: require "lazuli.modules.user_management.views.register"
  [register_do: "/register/do"]: capture_errors{
    on_error: => render: require "lazuli.modules.user_management.views.register_do", status: 403
    =>
      csrf.assert_token @, "lazuli_modules_usermanagement"
      assert_valid @params, {
        { "username", exists: true, min_length: 2, max_length: 250, user_doesnt_exist: true }
        { "password", exists: true, min_length: 4 }
        { "password_repeat", equals: @params.password, "passwords must match" }
      }
      user = Users\create {
        username: @params.username
        pwHMACs1: encode_base64(hmac_sha1(@params.password,@params.username..@params.password))
      }
      render: require "lazuli.modules.user_management.views.register_do"
  }
  [login: "/login"]: cached exptime: 60,[1]:=>
    render: require "lazuli.modules.user_management.views.login"
  [logout: "/logout"]: =>
    @session.modules.user_management.currentuser=nil
    @modules.user_management.currentuser=nil
    render: require "lazuli.modules.user_management.views.logout"
  [login_do: "/login/do"]: capture_errors{
    on_error: => render: require "lazuli.modules.user_management.views.login_do", status: 403
    =>
      csrf.assert_token @, "lazuli_modules_usermanagement"
      assert_valid @params, {
        { "username", exists: true, min_length: 2, max_length: 250, user_exists: true}
        { "password", exists: true, min_length: 4 }
      }
      user = Users\find username: @params.username
      logged_in_ok=false
      user.logged_in_by_provider="fallback"
      user.logged_in_providers_tried={}
      for k,v in pairs @modules.user_management.providers
        ret,msg=v\tryLogin(user,@params)
        switch ret
          when true
            logged_in_ok=true
            user.logged_in_by_provider=k
            user.logged_in_providers_tried[k]={true,msg}
            break
          when false
            user.logged_in_providers_tried[k]={false,msg}
          when nil
            assert_error nil, msg
      if not logged_in_ok
        assert_error user.pwHMACs1==encode_base64(hmac_sha1(@params.password,user.username..@params.password)), "wrong password"
      @session.modules.user_management.currentuser=user.id
      @modules.user_management.currentuser=user
      render: require "lazuli.modules.user_management.views.login_do"
  }

lazuli = require "lazuli"
import capture_errors,assert_error from require "lapis.application"
import validate_functions, assert_valid from require "lapis.validate"
import hmac_sha1 from require "lapis.util.encoding"
import encode_base64 from require "lapis.util.encoding"
import cached from require "lapis.cache"
csrf = require "lapis.csrf"
config = (require "lapis.config").get!

Users=require "lazuli.modules.user_management.models.users"

validate_functions.user_doesnt_exist = (username) ->
  u=Users\find username: username
  return not u, "user name already taken"
validate_functions.user_exists = (username) ->
  u=Users\find username: username
  return u and true or false, "user does not exist"

class UsersApplication extends lazuli.Application
  @path: "/users"
  @name: "modules_user_management_"
  @before_filter =>
    @modules.user_management or={}
    @session.modules.user_management or={}
    if not @modules.user_management.providers
      if type(config.modules.user_management)=="table" and type(config.modules.user_management.providers)=="table"
        @modules.user_management.providers={n,require(n)(@) for n in *config.modules.user_management.providers}
    @modules.user_management.csrf_token = csrf.generate_token @, "lazuli_modules_usermanagement"
    if @session.modules.user_management.currentuser and not @modules.user_management.currentuser
      @modules.user_management.currentuser = Users\find @session.modules.user_management.currentuser
  [register: "/register"]: cached exptime: 60,[1]:=>
    render: require "lazuli.modules.user_management.views.register"
  [register_do: "/register/do"]: capture_errors{
    on_error: => render: require "lazuli.modules.user_management.views.register_do", status: 403
    =>
      csrf.assert_token @, "lazuli_modules_usermanagement"
      assert_valid @params, {
        { "username", exists: true, min_length: 2, max_length: 250, user_doesnt_exist: true }
        { "password", exists: true, min_length: 4 }
        { "password_repeat", equals: @params.password, "passwords must match" }
      }
      user = Users\create {
        username: @params.username
        pwHMACs1: encode_base64(hmac_sha1(@params.password,@params.username..@params.password))
      }
      render: require "lazuli.modules.user_management.views.register_do"
  }
  [login: "/login"]: cached exptime: 60,[1]:=>
    render: require "lazuli.modules.user_management.views.login"
  [logout: "/logout"]: =>
    @session.modules.user_management.currentuser=nil
    @modules.user_management.currentuser=nil
    render: require "lazuli.modules.user_management.views.logout"
  [login_do: "/login/do"]: capture_errors{
    on_error: => render: require "lazuli.modules.user_management.views.login_do", status: 403
    =>
      csrf.assert_token @, "lazuli_modules_usermanagement"
      assert_valid @params, {
        { "username", exists: true, min_length: 2, max_length: 250, user_exists: true}
        { "password", exists: true, min_length: 4 }
      }
      user = Users\find username: @params.username
      logged_in_ok=false
      user.logged_in_by_provider="fallback"
      user.logged_in_providers_tried={}
      for k,v in pairs @modules.user_management.providers
        ret,msg=v\tryLogin(user,@params)
        switch ret
          when true
            logged_in_ok=true
            user.logged_in_by_provider=k
            user.logged_in_providers_tried[k]={true,msg}
            break
          when false
            user.logged_in_providers_tried[k]={false,msg}
          when nil
            assert_error nil, msg
      if not logged_in_ok
        assert_error user.pwHMACs1==encode_base64(hmac_sha1(@params.password,user.username..@params.password)), "wrong password"
      @session.modules.user_management.currentuser=user.id
      @modules.user_management.currentuser=user
      render: require "lazuli.modules.user_management.views.login_do"
  }


logger: support conversion from iso to utf

Former-commit-id: bad284ce8bd5a1cc330b79ef62c42d9fe0366315 [formerly 78a63d49e6065086efcf6203d596bf346ce5cd4d]
Former-commit-id: 3b5043204cc85fdf05eae04caac126789e44f746

dbi = require 'DBI'
require'logging.console'
iconv = require'iconv'
iso2utf = iconv.new('UTF-8'.."//TRANSLIT", 'ISO-8859-1')
log = logging.console()

conn = false

schemas = {[[
  CREATE TABLE IF NOT EXISTS log (
    time timestamp DEFAULT now(),
    nick text,
    channel text,
    message text,
    type text
  );]],
  [[CREATE INDEX idx_time ON log(time);]],
  [[CREATE INDEX idx_channel ON log(channel);]]
}

toutf = (s) ->
  iso2utf\iconv(s)

connect = ->
  conn, err = DBI.Connect('PostgreSQL', ivar2.config.dbname, ivar2.config.dbuser, ivar2.config.dbpass, ivar2.config.dbhost, ivar2.config.dbport)
  unless conn
    log\error "Unable to connect to DB: #{err}"
    return

  conn\autocommit(true)

  --for s in *schemas
  --  a,b = DBI.Do(conn, s)

dbh = ->
  connect! unless conn

  -- Check if connection is alive
  alive = conn\ping!
  connect! unless alive

  success, err = DBI.Do(conn, 'SELECT NOW()')
  unless success
    log\error "SQL Connection :#{err}"
    connect!

  return conn

dblog = (type, source, destination, arg) =>
  nick = source.nick

  if type == 'PRIVMSG'
    -- action
    if arg\sub(1,1) == '\001' and arg\sub(-1) == '\001'
      arg = arg\sub(9, -2)
      type = 'ACTION'

  unless arg
    arg = ''

  insert = ->
    ins = dbh!\prepare('INSERT INTO log(nick,channel,message,type) values(?,?,?,?)')
    ins\execute(nick, destination, arg, type)

  -- First try to insert statement directly
  -- if that doesn't work; convert it from iso to utf and try again
  stmt, err = insert!
  unless stmt
    arg, err = toutf(arg)
    stmt, err = insert!
    unless stmt
      log\error err

history = (source, destination, nr) ->
  nr = tonumber(nr) or 1
  stmt = dbh!\prepare [[
    SELECT
      *
      FROM log
      WHERE channel=?
      AND (type = 'PRIVMSG' OR type = 'ACTION')
      ORDER BY time
      DESC LIMIT ?
    ]]
  stmt\execute destination, nr
  out = {}
  for row in stmt\rows(true) -- true for column names
    out[#out+1] = row

  if #out == 1
    return out[1].message
  return out

lastlog = (source, destination, arg) ->
  nr = tonumber(nr) or 1
  arg = '%'..arg..'%'
  stmt = dbh!\prepare [[
    SELECT
      *
      FROM log
      WHERE channel=?
      AND (type = 'PRIVMSG' OR type = 'ACTION')
      AND message LIKE ?
      ORDER BY time DESC
      LIMIT 20
    ]]
  stmt\execute destination, arg
  out = {}
  for row in stmt\rows(true) -- true for column names
    out[#out+1] = row

  if #out == 1
    return out[1].message
  return out

return {
  PRIVMSG: {
    (s, d, a) =>
      dblog @, 'PRIVMSG', s, d, a
    '%plast$': (source, destination) =>
      say history(source,destination,1)
    '%plastlog (.+)$': (source, destination, arg) =>
      out = {}
      for k,v in *lastlog(source, destination, arg)
        table.insert out, string.format('<%s> %s', k.nick, k.message)
      say table.concat(out, ' ')
  }
  NOTICE: {
    (s, d, a) =>
      dblog @, 'NOTICE', s, d, a
  }
  JOIN: {
    (s, d, a) =>
      dblog @, 'JOIN', s, d, a
  }
  PART: {
    (s, d, a) =>
      dblog @, 'PART', s, d, a
  }
  KICK: {
    (s, d, a) =>
      dblog @, 'KICK', s, d, a
  }
  MODE: {
    (s, d, a) =>
      dblog @, 'MODE', s, d, a
  }
  TOPIC: {
    (s, d, a) =>
      dblog @, 'TOPIC', s, d, a
  }
  NICK: {
    (s, d, a) =>
      dblog @, 'NICK', s, d, a
  }
  QUIT: {
    (s, d, a) =>
      dblog @, 'QUIT', s, d, a
  }
}

dbi = require 'DBI'
require'logging.console'
log = logging.console()

conn = false

schemas = {[[
  CREATE TABLE IF NOT EXISTS log (
    time timestamp DEFAULT now(),
    nick text,
    channel text,
    message text,
    type text
  );]],
  [[CREATE INDEX idx_time ON log(time);]],
  [[CREATE INDEX idx_channel ON log(channel);]]
}

connect = ->
  conn, err = DBI.Connect('PostgreSQL', ivar2.config.dbname, ivar2.config.dbuser, ivar2.config.dbpass, ivar2.config.dbhost, ivar2.config.dbport)
  unless conn
    log\error "Unable to connect to DB: #{err}"
    return

  conn\autocommit(true)

  --for s in *schemas
  --  a,b = DBI.Do(conn, s)

dbh = ->
  connect! unless conn

  -- Check if connection is alive
  alive = conn\ping!
  connect! unless alive

  success, err = DBI.Do(conn, 'SELECT NOW()')
  unless success
    log\error "SQL Connection :#{err}"
    connect!

  return conn

dblog = (type, source, destination, arg) =>
  nick = source.nick

  if type == 'PRIVMSG'
    -- action
    if arg\sub(1,1) == '\001' and arg\sub(-1) == '\001'
      arg = arg\sub(9, -2)
      type = 'ACTION'

  unless arg
    arg = ''

  insert = dbh!\prepare('INSERT INTO log(nick,channel,message,type) values(?,?,?,?)')
  stmt, err = insert\execute(nick, destination, arg, type)
  unless stmt
    log\error err

history = (source, destination, nr) ->
  nr = tonumber(nr) or 1
  stmt = dbh!\prepare [[
    SELECT
      *
      FROM log
      WHERE channel=?
      AND (type = 'PRIVMSG' OR type = 'ACTION')
      ORDER BY time
      DESC LIMIT ?
    ]]
  stmt\execute destination, nr
  out = {}
  for row in stmt\rows(true) -- true for column names
    out[#out+1] = row

  if #out == 1
    return out[1].message
  return out

lastlog = (source, destination, arg) ->
  nr = tonumber(nr) or 1
  arg = '%'..arg..'%'
  stmt = dbh!\prepare [[
    SELECT
      *
      FROM log
      WHERE channel=?
      AND (type = 'PRIVMSG' OR type = 'ACTION')
      AND message LIKE ?
      ORDER BY time DESC
      LIMIT 20
    ]]
  stmt\execute destination, arg
  out = {}
  for row in stmt\rows(true) -- true for column names
    out[#out+1] = row

  if #out == 1
    return out[1].message
  return out

return {
  PRIVMSG: {
    (s, d, a) =>
      dblog @, 'PRIVMSG', s, d, a
    '%plast$': (source, destination) =>
      say history(source,destination,1)
    '%plastlog (.+)$': (source, destination, arg) =>
      out = {}
      for k,v in *lastlog(source, destination, arg)
        table.insert out, string.format('<%s> %s', k.nick, k.message)
      say table.concat(out, ' ')
  }
  NOTICE: {
    (s, d, a) =>
      dblog @, 'NOTICE', s, d, a
  }
  JOIN: {
    (s, d, a) =>
      dblog @, 'JOIN', s, d, a
  }
  PART: {
    (s, d, a) =>
      dblog @, 'PART', s, d, a
  }
  KICK: {
    (s, d, a) =>
      dblog @, 'KICK', s, d, a
  }
  MODE: {
    (s, d, a) =>
      dblog @, 'MODE', s, d, a
  }
  TOPIC: {
    (s, d, a) =>
      dblog @, 'TOPIC', s, d, a
  }
  NICK: {
    (s, d, a) =>
      dblog @, 'NICK', s, d, a
  }
  QUIT: {
    (s, d, a) =>
      dblog @, 'QUIT', s, d, a
  }
}


Added toString to geometry.Point

Added toString to geometry.Point

export ^

class Point
	new: (x = 0, y = 0) =>
		-- Add the clone mixin
		clone = require "LunoPunk.utils.mixins.clone"
		clone @
		@x, @y = x, y

	toString: => string.format "(%f, %f)", @x, @y

	__tostring: => @toString!


Update objects

Update objects

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

PPM2.TransformNewModelID = (id = 0) ->
    bgID = id % 17
    maneModelID = math.floor(id / 16 - .01) + 1
    maneModelID = 1 if maneModelID == 0
    return maneModelID, bgID

OLD_NW_OBJECTS = {}

for ply in *player.GetAll()
    if ply\GetPonyData()
        table.insert(OLD_NW_OBJECTS, ply\GetPonyData())

class NetworkedPonyData extends PPM2.NetworkedObject
    @NW_ClientsideCreation = true

    @Setup()
    @NetworkVar('Entity',           net.ReadEntity, net.WriteEntity, NULL)
    @NetworkVar('UpperManeModel',   net.ReadEntity, net.WriteEntity, NULL)
    @NetworkVar('LowerManeModel',   net.ReadEntity, net.WriteEntity, NULL)
    @NetworkVar('TailModel',        net.ReadEntity, net.WriteEntity, NULL)
    @NetworkVar('SocksModel',       net.ReadEntity, net.WriteEntity, NULL)

    @NetworkVar('Race',             (-> math.Clamp(net.ReadUInt(4), 0, 3)), ((arg = PPM2.RACE_EARTH) -> net.WriteUInt(arg, 4)), PPM2.RACE_EARTH)
    @NetworkVar('Gender',           (-> math.Clamp(net.ReadUInt(4), 0, 1)), ((arg = PPM2.GENDER_FEMALE) -> net.WriteUInt(arg, 4)), PPM2.GENDER_FEMALE)
    @NetworkVar('Weight',           (-> math.Clamp(net.ReadFloat(), PPM2.MIN_WEIGHT, PPM2.MAX_WEIGHT)), net.WriteFloat, 1)

    -- Reserved - they can be accessed/used/changed, but they do not do anything
    @NetworkVar('Age',              (-> math.Clamp(net.ReadUInt(4), 0, 2)), ((arg = PPM2.AGE_ADULT) -> net.WriteUInt(arg, 4)), PPM2.AGE_ADULT)

    @NetworkVar('EyelashType',      (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_EYELASHES, PPM2.MAX_EYELASHES)),           ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('TailType',         (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_TAILS, PPM2.MAX_TAILS)),                   ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('ManeType',         (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_UPPER_MANES, PPM2.MAX_UPPER_MANES)),       ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('ManeTypeLower',    (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_LOWER_MANES, PPM2.MAX_LOWER_MANES)),       ((arg = 0) -> net.WriteUInt(arg, 8)), 0)

    @NetworkVar('TailTypeNew',      (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_TAILS_NEW, PPM2.MAX_TAILS_NEW)),               ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('ManeTypeNew',      (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_UPPER_MANES_NEW, PPM2.MAX_UPPER_MANES_NEW)),   ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('ManeTypeLowerNew', (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_LOWER_MANES_NEW, PPM2.MAX_LOWER_MANES_NEW)),   ((arg = 0) -> net.WriteUInt(arg, 8)), 0)

    @NetworkVar('BodyColor',        net.ReadColor, net.WriteColor, 	    Color(255, 255, 255))

    @NetworkVar('SeparateEyes',     net.ReadBool, net.WriteBool,              false)
    for publicName in *{'', 'Left', 'Right'}
        @NetworkVar("EyeType#{publicName}",          (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_EYE_TYPE, PPM2.MAX_EYE_TYPE)), ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
        @NetworkVar("EyeBackground#{publicName}",    net.ReadColor, net.WriteColor, 	    Color(255, 255, 255))
        @NetworkVar("EyeHole#{publicName}",          net.ReadColor, net.WriteColor, 	    Color(0,   0,   0  ))
        @NetworkVar("EyeIrisTop#{publicName}",       net.ReadColor, net.WriteColor, 	    Color(200, 200, 200))
        @NetworkVar("EyeIrisBottom#{publicName}",    net.ReadColor, net.WriteColor, 	    Color(200, 200, 200))
        @NetworkVar("EyeIrisLine1#{publicName}",     net.ReadColor, net.WriteColor, 	    Color(255, 255, 255))
        @NetworkVar("EyeIrisLine2#{publicName}",     net.ReadColor, net.WriteColor, 	    Color(255, 255, 255))
        @NetworkVar("EyeReflection#{publicName}",    net.ReadColor, net.WriteColor,    Color(255, 255, 255, 127))
        @NetworkVar("EyeEffect#{publicName}",        net.ReadColor, net.WriteColor,         Color(255, 255, 255))
        @NetworkVar("EyeLines#{publicName}",         net.ReadBool, net.WriteBool,                           true)
        @NetworkVar("DerpEyes#{publicName}",         net.ReadBool, net.WriteBool,                          false)
        @NetworkVar("DerpEyesStrength#{publicName}", (-> math.Clamp(net.ReadFloat(), PPM2.MIN_DERP_STRENGTH, PPM2.MAX_DERP_STRENGTH)), net.WriteFloat, 1)
        @NetworkVar("HoleWidth#{publicName}",        (-> math.Clamp(net.ReadFloat(), PPM2.MIN_PUPIL_SIZE, PPM2.MAX_PUPIL_SIZE)), net.WriteFloat, 1)
        @NetworkVar("HoleSize#{publicName}",         (-> math.Clamp(net.ReadFloat(), PPM2.MIN_HOLE, PPM2.MAX_HOLE)),             net.WriteFloat, .8)
        @NetworkVar("IrisSize#{publicName}",         (-> math.Clamp(net.ReadFloat(), PPM2.MIN_IRIS, PPM2.MAX_IRIS)),             net.WriteFloat, .8)

    @NetworkVar('SeparateMane',     net.ReadBool, net.WriteBool,              false)
    for i = 1, 6
        @NetworkVar("ManeColor#{i}",            net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("ManeDetailColor#{i}",      net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("ManeURLColor#{i}",         net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("ManeURL#{i}",              net.ReadString, net.WriteString,   '')

        @NetworkVar("TailColor#{i}",            net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("TailDetailColor#{i}",      net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("TailURLColor#{i}",         net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("TailURL#{i}",              net.ReadString, net.WriteString,   '')

        @NetworkVar("LowerManeColor#{i}",       net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("LowerManeURL#{i}",         net.ReadString, net.WriteString,   '')
        @NetworkVar("LowerManeURLColor#{i}",    net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("LowerManeDetailColor#{i}", net.ReadColor, net.WriteColor,     Color(255, 255, 255))

        @NetworkVar("UpperManeColor#{i}",       net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("UpperManeURL#{i}",         net.ReadString, net.WriteString,   '')
        @NetworkVar("UpperManeDetailColor#{i}", net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("UpperManeURLColor#{i}",    net.ReadColor, net.WriteColor,     Color(255, 255, 255))

    @NetworkVar('CMark',            net.ReadBool, net.WriteBool,              true)
    @NetworkVar('CMarkURL',         net.ReadString, net.WriteString,            '')
    @NetworkVar('CMarkType',        (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_CMARK, PPM2.MAX_CMARK)),           ((arg = 4) -> net.WriteUInt(arg, 8)), 4)
    @NetworkVar('TailSize',         (-> math.Clamp(net.ReadFloat(), PPM2.MIN_TAIL_SIZE, PPM2.MAX_TAIL_SIZE)),   net.WriteFloat, 1)

    @NetworkVar('Bodysuit',         (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_SUIT, PPM2.MAX_SUIT)),             ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('Socks',            net.ReadBool, net.WriteBool,              false)
    @NetworkVar('NoFlex',           net.ReadBool, net.WriteBool,              false)
    @NetworkVar('SocksAsModel',     net.ReadBool, net.WriteBool,              false)
    @NetworkVar('SocksColor',       net.ReadColor, net.WriteColor,            Color(255, 255, 255))

    for {:flex, :active} in *PPM2.PonyFlexController.FLEX_LIST
        @NetworkVar("DisableFlex#{flex}", net.ReadBool, net.WriteBool, false) if active

    for i = 1, PPM2.MAX_BODY_DETAILS
        @NetworkVar("BodyDetail#{i}",       (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_DETAIL, PPM2.MAX_DETAIL)), ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
        @NetworkVar("BodyDetailColor#{i}",  net.ReadColor,  net.WriteColor, Color(255, 255, 255))
        @NetworkVar("BodyDetailURL#{i}",    net.ReadString, net.WriteString, '')
        @NetworkVar("BodyDetailURLColor#{i}",  net.ReadColor,  net.WriteColor, Color(255, 255, 255))

    new: (netID, ent) =>
        @recomputeTextures = true
        if ent
            @modelCached = ent\GetModel()
            @SetEntity(ent)
            @SetupEntity(ent)
        super(netID)
    GetModel: => @modelCached
    EntIndex: => @entID
    SetupEntity: (ent) =>
        return unless IsValid(ent)
        @modelCached = ent\GetModel()
        @ent = ent
        ent.__PPM2_PonyData = @
        @entID = ent\EntIndex()
        @ModelChanges(@modelCached, @modelCached)
        if CLIENT
            timer.Simple(0, ->
                @GetRenderController()\CompileTextures()
            )
    ModelChanges: (old = @ent\GetModel(), new = old) =>
        @modelCached = new
        timer.Simple 0.5, ->
            return unless IsValid(@ent)
            @GetBodygroupController()\ApplyBodygroups()
            if CLIENT
                @GetRenderController()
                @GetWeightController()
    GenericDataChange: (state) =>
        if state\GetKey() == 'Entity' and IsValid(@GetEntity())
            @SetupEntity(@GetEntity())

        @GetBodygroupController()\DataChanges(state) if @ent

        if CLIENT and @ent
            @GetWeightController()\DataChanges(state)
            @GetRenderController()\DataChanges(state)
    Think: =>
    PlayerRespawn: =>
        if CLIENT
            @GetWeightController()\UpdateWeight()
            @GetRenderController()\PlayerRespawn()
    ApplyBodygroups: => @GetBodygroupController()\ApplyBodygroups() if @ent
    SetLocalChange: (state) => @GenericDataChange(state)
    NetworkDataChanges: (state) => @GenericDataChange(state)
    GetRenderController: =>
        return if SERVER
        if not @renderController or @modelCached ~= @modelRender
            cls = PPM2.GetRenderController(@modelCached)
            @modelRender = @modelCached
            @renderController = cls(@)
        @renderController.ent = @ent
        return @renderController
    GetWeightController: =>
        return if SERVER
        if not @weightController or @modelCached ~= @modelWeight
            cls = PPM2.GetPonyWeightController(@modelCached)
            @modelWeight = @modelCached
            @weightController = cls(@)
        @weightController.ent = @ent
        return @weightController
    GetBodygroupController: =>
        if not @bodygroups or @modelBodygroups ~= @modelCached
            @modelCached = @modelCached or @ent\GetModel()
            cls = PPM2.GetBodugroupController(@modelCached)
            @bodygroups = cls(@)
            @modelBodygroups = @modelCached
        @bodygroups.ent = @ent
        return @bodygroups

for obj in *OLD_NW_OBJECTS
    obj.__class = NetworkedPonyData
    NetworkedPonyData.NW_Objects[obj.netID] = obj if obj.netID >= 0
OLD_NW_OBJECTS = nil

PPM2.NetworkedPonyData = NetworkedPonyData

entMeta = FindMetaTable('Entity')
entMeta.GetPonyData = =>
    if @__PPM2_PonyData and @__PPM2_PonyData\GetEntity() ~= @
        @__PPM2_PonyData\SetEntity(@)
        @__PPM2_PonyData\SetupEntity(@) if CLIENT
    return @__PPM2_PonyData

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

PPM2.TransformNewModelID = (id = 0) ->
    bgID = id % 17
    maneModelID = math.floor(id / 16 - .01) + 1
    maneModelID = 1 if maneModelID == 0
    return maneModelID, bgID

class NetworkedPonyData extends PPM2.NetworkedObject
    @NW_ClientsideCreation = true

    @Setup()
    @NetworkVar('Entity',           net.ReadEntity, net.WriteEntity, NULL)
    @NetworkVar('UpperManeModel',   net.ReadEntity, net.WriteEntity, NULL)
    @NetworkVar('LowerManeModel',   net.ReadEntity, net.WriteEntity, NULL)
    @NetworkVar('TailModel',        net.ReadEntity, net.WriteEntity, NULL)
    @NetworkVar('SocksModel',       net.ReadEntity, net.WriteEntity, NULL)

    @NetworkVar('Race',             (-> math.Clamp(net.ReadUInt(4), 0, 3)), ((arg = PPM2.RACE_EARTH) -> net.WriteUInt(arg, 4)), PPM2.RACE_EARTH)
    @NetworkVar('Gender',           (-> math.Clamp(net.ReadUInt(4), 0, 1)), ((arg = PPM2.GENDER_FEMALE) -> net.WriteUInt(arg, 4)), PPM2.GENDER_FEMALE)
    @NetworkVar('Weight',           (-> math.Clamp(net.ReadFloat(), PPM2.MIN_WEIGHT, PPM2.MAX_WEIGHT)), net.WriteFloat, 1)

    -- Reserved - they can be accessed/used/changed, but they do not do anything
    @NetworkVar('Age',              (-> math.Clamp(net.ReadUInt(4), 0, 2)), ((arg = PPM2.AGE_ADULT) -> net.WriteUInt(arg, 4)), PPM2.AGE_ADULT)

    @NetworkVar('EyelashType',      (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_EYELASHES, PPM2.MAX_EYELASHES)),           ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('TailType',         (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_TAILS, PPM2.MAX_TAILS)),                   ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('ManeType',         (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_UPPER_MANES, PPM2.MAX_UPPER_MANES)),       ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('ManeTypeLower',    (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_LOWER_MANES, PPM2.MAX_LOWER_MANES)),       ((arg = 0) -> net.WriteUInt(arg, 8)), 0)

    @NetworkVar('TailTypeNew',      (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_TAILS_NEW, PPM2.MAX_TAILS_NEW)),               ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('ManeTypeNew',      (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_UPPER_MANES_NEW, PPM2.MAX_UPPER_MANES_NEW)),   ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('ManeTypeLowerNew', (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_LOWER_MANES_NEW, PPM2.MAX_LOWER_MANES_NEW)),   ((arg = 0) -> net.WriteUInt(arg, 8)), 0)

    @NetworkVar('BodyColor',        net.ReadColor, net.WriteColor, 	    Color(255, 255, 255))

    @NetworkVar('SeparateEyes',     net.ReadBool, net.WriteBool,              false)
    for publicName in *{'', 'Left', 'Right'}
        @NetworkVar("EyeType#{publicName}",          (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_EYE_TYPE, PPM2.MAX_EYE_TYPE)), ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
        @NetworkVar("EyeBackground#{publicName}",    net.ReadColor, net.WriteColor, 	    Color(255, 255, 255))
        @NetworkVar("EyeHole#{publicName}",          net.ReadColor, net.WriteColor, 	    Color(0,   0,   0  ))
        @NetworkVar("EyeIrisTop#{publicName}",       net.ReadColor, net.WriteColor, 	    Color(200, 200, 200))
        @NetworkVar("EyeIrisBottom#{publicName}",    net.ReadColor, net.WriteColor, 	    Color(200, 200, 200))
        @NetworkVar("EyeIrisLine1#{publicName}",     net.ReadColor, net.WriteColor, 	    Color(255, 255, 255))
        @NetworkVar("EyeIrisLine2#{publicName}",     net.ReadColor, net.WriteColor, 	    Color(255, 255, 255))
        @NetworkVar("EyeReflection#{publicName}",    net.ReadColor, net.WriteColor,    Color(255, 255, 255, 127))
        @NetworkVar("EyeEffect#{publicName}",        net.ReadColor, net.WriteColor,         Color(255, 255, 255))
        @NetworkVar("EyeLines#{publicName}",         net.ReadBool, net.WriteBool,                           true)
        @NetworkVar("DerpEyes#{publicName}",         net.ReadBool, net.WriteBool,                          false)
        @NetworkVar("DerpEyesStrength#{publicName}", (-> math.Clamp(net.ReadFloat(), PPM2.MIN_DERP_STRENGTH, PPM2.MAX_DERP_STRENGTH)), net.WriteFloat, 1)
        @NetworkVar("HoleWidth#{publicName}",        (-> math.Clamp(net.ReadFloat(), PPM2.MIN_PUPIL_SIZE, PPM2.MAX_PUPIL_SIZE)), net.WriteFloat, 1)
        @NetworkVar("HoleSize#{publicName}",         (-> math.Clamp(net.ReadFloat(), PPM2.MIN_HOLE, PPM2.MAX_HOLE)),             net.WriteFloat, .8)
        @NetworkVar("IrisSize#{publicName}",         (-> math.Clamp(net.ReadFloat(), PPM2.MIN_IRIS, PPM2.MAX_IRIS)),             net.WriteFloat, .8)

    @NetworkVar('SeparateMane',     net.ReadBool, net.WriteBool,              false)
    for i = 1, 6
        @NetworkVar("ManeColor#{i}",            net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("ManeDetailColor#{i}",      net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("ManeURLColor#{i}",         net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("ManeURL#{i}",              net.ReadString, net.WriteString,   '')

        @NetworkVar("TailColor#{i}",            net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("TailDetailColor#{i}",      net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("TailURLColor#{i}",         net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("TailURL#{i}",              net.ReadString, net.WriteString,   '')

        @NetworkVar("LowerManeColor#{i}",       net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("LowerManeURL#{i}",         net.ReadString, net.WriteString,   '')
        @NetworkVar("LowerManeURLColor#{i}",    net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("LowerManeDetailColor#{i}", net.ReadColor, net.WriteColor,     Color(255, 255, 255))

        @NetworkVar("UpperManeColor#{i}",       net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("UpperManeURL#{i}",         net.ReadString, net.WriteString,   '')
        @NetworkVar("UpperManeDetailColor#{i}", net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("UpperManeURLColor#{i}",    net.ReadColor, net.WriteColor,     Color(255, 255, 255))

    @NetworkVar('CMark',            net.ReadBool, net.WriteBool,              true)
    @NetworkVar('CMarkURL',         net.ReadString, net.WriteString,            '')
    @NetworkVar('CMarkType',        (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_CMARK, PPM2.MAX_CMARK)),           ((arg = 4) -> net.WriteUInt(arg, 8)), 4)
    @NetworkVar('TailSize',         (-> math.Clamp(net.ReadFloat(), PPM2.MIN_TAIL_SIZE, PPM2.MAX_TAIL_SIZE)),   net.WriteFloat, 1)

    @NetworkVar('Bodysuit',         (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_SUIT, PPM2.MAX_SUIT)),             ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('Socks',            net.ReadBool, net.WriteBool,              false)
    @NetworkVar('NoFlex',           net.ReadBool, net.WriteBool,              false)
    @NetworkVar('SocksAsModel',     net.ReadBool, net.WriteBool,              false)
    @NetworkVar('SocksColor',       net.ReadColor, net.WriteColor,            Color(255, 255, 255))

    for {:flex, :active} in *PPM2.PonyFlexController.FLEX_LIST
        @NetworkVar("DisableFlex#{flex}", net.ReadBool, net.WriteBool, false) if active

    for i = 1, PPM2.MAX_BODY_DETAILS
        @NetworkVar("BodyDetail#{i}",       (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_DETAIL, PPM2.MAX_DETAIL)), ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
        @NetworkVar("BodyDetailColor#{i}",  net.ReadColor,  net.WriteColor, Color(255, 255, 255))
        @NetworkVar("BodyDetailURL#{i}",    net.ReadString, net.WriteString, '')
        @NetworkVar("BodyDetailURLColor#{i}",  net.ReadColor,  net.WriteColor, Color(255, 255, 255))

    new: (netID, ent) =>
        @recomputeTextures = true
        if ent
            @modelCached = ent\GetModel()
            @SetEntity(ent)
            @SetupEntity(ent)
        super(netID)
    GetModel: => @modelCached
    EntIndex: => @entID
    SetupEntity: (ent) =>
        return unless IsValid(ent)
        @modelCached = ent\GetModel()
        @ent = ent
        ent.__PPM2_PonyData = @
        @entID = ent\EntIndex()
        @ModelChanges(@modelCached, @modelCached)
        if CLIENT
            timer.Simple(0, ->
                @GetRenderController()\CompileTextures()
            )
    ModelChanges: (old = @ent\GetModel(), new = old) =>
        @modelCached = new
        timer.Simple 0.5, ->
            return unless IsValid(@ent)
            @GetBodygroupController()\ApplyBodygroups()
            if CLIENT
                @GetRenderController()
                @GetWeightController()
    GenericDataChange: (state) =>
        if state\GetKey() == 'Entity' and IsValid(@GetEntity())
            @SetupEntity(@GetEntity())

        @GetBodygroupController()\DataChanges(state) if @ent

        if CLIENT and @ent
            @GetWeightController()\DataChanges(state)
            @GetRenderController()\DataChanges(state)
    Think: =>
    PlayerRespawn: =>
        if CLIENT
            @GetWeightController()\UpdateWeight()
            @GetRenderController()\PlayerRespawn()
    ApplyBodygroups: => @GetBodygroupController()\ApplyBodygroups() if @ent
    SetLocalChange: (state) => @GenericDataChange(state)
    NetworkDataChanges: (state) => @GenericDataChange(state)
    GetRenderController: =>
        return if SERVER
        if not @renderController or @modelCached ~= @modelRender
            cls = PPM2.GetRenderController(@modelCached)
            @modelRender = @modelCached
            @renderController = cls(@)
        @renderController.ent = @ent
        return @renderController
    GetWeightController: =>
        return if SERVER
        if not @weightController or @modelCached ~= @modelWeight
            cls = PPM2.GetPonyWeightController(@modelCached)
            @modelWeight = @modelCached
            @weightController = cls(@)
        @weightController.ent = @ent
        return @weightController
    GetBodygroupController: =>
        if not @bodygroups or @modelBodygroups ~= @modelCached
            @modelCached = @modelCached or @ent\GetModel()
            cls = PPM2.GetBodugroupController(@modelCached)
            @bodygroups = cls(@)
            @modelBodygroups = @modelCached
        @bodygroups.ent = @ent
        return @bodygroups

PPM2.NetworkedPonyData = NetworkedPonyData

entMeta = FindMetaTable('Entity')
entMeta.GetPonyData = =>
    if @__PPM2_PonyData and @__PPM2_PonyData\GetEntity() ~= @
        @__PPM2_PonyData\SetEntity(@)
        @__PPM2_PonyData\SetupEntity(@) if CLIENT
    return @__PPM2_PonyData


github: use compare URL instead of URL to individual commit

Former-commit-id: 66fcfd6d5cfe1f4ef6b872f1f12b195f62e4db1a [formerly 1aa3c0dca5ef9c1d82b9f1afebefa2b8b4a3d483]
Former-commit-id: cf0df704a015d31ba2b3b5a286ccf4f7602e1c91

util = require 'util'

hex_to_char = (x) ->
  string.char(tonumber(x, 16))

unescape = (url) ->
  url\gsub("%%(%x%x)", hex_to_char)

handlers = {
  push: (repo, destination, json) ->
    branch = util.bold(json.ref\gsub 'refs/heads/', '')
    for i, c in pairs(json.commits)
      if i > 3
        break
      message = c.message\gsub '\n.*', ''
      if i == 1
        message = message .. ' ' .. json.compare
      ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{branch}, #{util.nonickalertword c.author.username}: #{message}"
    if #json.commits > 3
      ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{branch}, #{#json.commits-3} more commits not displayed."
  watch: (repo, destination, json) ->
    action = json.action
    if action == 'started'
      ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: +1 ★ (Total: #{json.repository.stargazers_count})"
    else
      ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: #{action} watching"
  ping: (repo, destination, json) ->
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: ping"
  commit_comment: (repo, destination, json) ->
    --commenter = json.comment.user.login
    body = json.comment.body
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: commented #{body}"
  pull_request: (repo, destination, json) ->
    action = json.action
    number = util.bold json.number
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: #{action} PR ##{number}: #{json.pull_request.title} #{json.pull_request.html_url}"
  issues: (repo, destination, json) ->
    action = json.action
    nr = util.bold json.issue.number
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: #{action} issue ##{nr}: #{json.issue.title} #{json.issue.html_url}"
  issue_comment: (repo, destination, json) ->
    action = json.action
    nr = util.bold json.issue.number
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: comment on issue ##{nr}: #{json.comment.body} #{json.issue.html_url}"
  fork: (repo, destination, json) ->
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: forked to #{json.forkee.html_url}"
  create: (repo, destination, json) ->
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: created #{json.ref_type} #{util.bold json.ref}"
  delete: (repo, destination, json) ->
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: deleted #{json.ref_type} #{util.bold json.ref}"
  gollum: (repo, destination, json) ->
    return
  release: (repo, destination, json) ->
    return
}

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

  repo = json.repository.full_name

  event = req.headers['X-GitHub-Event']
  handler = handlers[event]
  if handler
    handler(repo, destination, json)
  else
    ivar2\Log('error', 'Unknown github event: %s', event)

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

return {}

util = require 'util'

hex_to_char = (x) ->
  string.char(tonumber(x, 16))

unescape = (url) ->
  url\gsub("%%(%x%x)", hex_to_char)

handlers = {
  push: (repo, destination, json) ->
    branch = util.bold(json.ref\gsub 'refs/heads/', '')
    for i, c in pairs(json.commits)
      if i > 3
        break
      message = c.message\gsub '\n.*', ''
      ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{branch}, #{util.nonickalertword c.author.username}: #{message} #{c.url}"
    if #json.commits > 3
      ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{branch}, #{#json.commits-3} more commits not displayed."
  watch: (repo, destination, json) ->
    action = json.action
    if action == 'started'
      ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: +1 ★ (Total: #{json.repository.stargazers_count})"
    else
      ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: #{action} watching"
  ping: (repo, destination, json) ->
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: ping"
  commit_comment: (repo, destination, json) ->
    --commenter = json.comment.user.login
    body = json.comment.body
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: commented #{body}"
  pull_request: (repo, destination, json) ->
    action = json.action
    number = util.bold json.number
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: #{action} PR ##{number}: #{json.pull_request.title} #{json.pull_request.html_url}"
  issues: (repo, destination, json) ->
    action = json.action
    nr = util.bold json.issue.number
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: #{action} issue ##{nr}: #{json.issue.title} #{json.issue.html_url}"
  issue_comment: (repo, destination, json) ->
    action = json.action
    nr = util.bold json.issue.number
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: comment on issue ##{nr}: #{json.comment.body} #{json.issue.html_url}"
  fork: (repo, destination, json) ->
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: forked to #{json.forkee.html_url}"
  create: (repo, destination, json) ->
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: created #{json.ref_type} #{util.bold json.ref}"
  delete: (repo, destination, json) ->
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: deleted #{json.ref_type} #{util.bold json.ref}"
  gollum: (repo, destination, json) ->
    return
  release: (repo, destination, json) ->
    return
}

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

  repo = json.repository.full_name

  event = req.headers['X-GitHub-Event']
  handler = handlers[event]
  if handler
    handler(repo, destination, json)
  else
    ivar2\Log('error', 'Unknown github event: %s', event)

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

return {}


Use native entity ugh

Use native entity
ugh

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

ALLOW_FLIGHT = CreateConVar('ppm2_sv_flight', '1', {FCVAR_ARCHIVE, FCVAR_REPLICATED, FCVAR_NOTIFY}, 'Allow flight for pegasus and alicorns. It obeys PlayerNoClip hook.')
FORCE_ALLOW_FLIGHT = CreateConVar('ppm2_sv_flight_force', '0', {FCVAR_ARCHIVE, FCVAR_REPLICATED, FCVAR_NOTIFY}, 'Ignore PlayerNoClip hook')
FLIGHT_DAMAGE = CreateConVar('ppm2_sv_flightdmg', '1', {FCVAR_ARCHIVE, FCVAR_REPLICATED, FCVAR_NOTIFY}, 'Damage players in flight')

class PonyflyController
    new: (data) =>
        @controller = data
        @ent = data.ent
        @speedMult = 0.5
        @speedMultDirections = 1.25
        @dragMult = 0.95
        @obbCenter = Vector(0, 0, 0)
        @obbMins = Vector(0, 0, 0)
        @obbMaxs = Vector(0, 0, 0)
        @speedMultDiv = 2
        @speedMultLift = 2
        @pitch = 0
        @yaw = 0
        @roll = 0
        @angleLerp = 1
        @lastVelocity = Vector(0, 0, 0)
        @lastState = false

    GetEntity: => @ent
    GetData: => @controller
    GetController: => @controller

    Switch: (status = false) =>
        @ent = @controller.ent
        return if not IsValid(@ent)
        return if @lastState == status
        @lastState = status
        if not status
            {:p, :y, :r} = @ent\EyeAngles()
            newAng = Angle(p, y, 0)
            @ent\SetEyeAngles(newAng)
            @ent\SetMoveType(MOVETYPE_WALK)
            @roll = 0
            @pitch = 0
            @yaw = 0
            @ent\SetVelocity(@lastVelocity * 50)
            @lastVelocity = Vector(0, 0, 0)
        else
            @lastVelocity = Vector(0, 0, 0)
            @ent\SetVelocity(-@ent\GetVelocity() * .97)
            @ent\SetMoveType(MOVETYPE_CUSTOM)
            @obbCenter = @ent\OBBCenter()
            @obbMins = @ent\OBBMins()
            @obbMaxs = @ent\OBBMaxs()
            @roll = 0
            @pitch = 0
            @yaw = 0
    Think: (movedata) =>
        pos     = movedata\GetOrigin()
        ang     = movedata\GetAngles()
        fwd     = ang\Forward()
        bcwd    = -ang\Forward()
        right   = ang\Right()
        left    = -ang\Right()
        up      = ang\Up()
        down    = -ang\Up()
        W       = movedata\KeyDown(IN_FORWARD)
        S       = movedata\KeyDown(IN_BACK)
        D       = movedata\KeyDown(IN_MOVERIGHT)
        A       = movedata\KeyDown(IN_MOVELEFT)
        CTRL    = movedata\KeyDown(IN_DUCK)
        MULT    = FrameTime() * 66

        velocity = movedata\GetVelocity()
        cSpeed = velocity\Length()
        cSpeed = 1 if cSpeed < 1
        dragSqrt = math.min(math.sqrt(cSpeed) / cSpeed * 2, 0.99)
        cSpeedMult = @speedMultDiv / cSpeed
        cSpeedMult = @speedMultDiv if cSpeedMult ~= cSpeedMult
        cSpeedLiftMult = @speedMultLift / cSpeed
        cSpeedLiftMult = @speedMultLift if cSpeedLiftMult ~= cSpeedLiftMult

        dragCalc = math.Clamp(@dragMult / dragSqrt, 0, 0.99)
        pitch = 0
        yaw = 0
        roll = 0

        hit = false
        hitLift = false

        if W
            velocity += fwd * MULT * @speedMult * cSpeedMult * @speedMultDirections
            hit = true
            pitch += 20

        if S
            velocity += bcwd * MULT * @speedMult * cSpeedMult * @speedMultDirections
            hit = true
            pitch -= 20

        if A
            velocity += left * MULT * @speedMult * cSpeedMult * @speedMultDirections
            hit = true
            roll -= 20

        if D
            velocity += right * MULT * @speedMult * cSpeedMult * @speedMultDirections
            hit = true
            roll += 20

        if CTRL
            velocity += Vector(0, 0, -MULT * @speedMult * cSpeedLiftMult)
            hitLift = true

        if @isLiftingUp
            velocity += Vector(0, 0, MULT * @speedMult * cSpeedLiftMult)
            hitLift = true

        if CLIENT
            lerpMult = FrameTime() * @angleLerp
            {:p, :y, :r} = ang
            p -= @pitch
            y -= @yaw
            @pitch = Lerp(lerpMult, @pitch, pitch)
            @yaw = Lerp(lerpMult, @yaw, yaw)
            @roll = Lerp(lerpMult, @roll, roll)
            p += @pitch
            y += @yaw
            r = @roll + math.sin(RealTime()) * 2
            newAng = Angle(p, y, r)
            @ent\SetEyeAngles(newAng)

        if not hit
            velocity.x *= dragCalc
            velocity.y *= dragCalc

        if not hitLift
            velocity.z *= dragCalc
            velocity.z += math.sin(RealTime() * 2) * .01

        pos += velocity

        movedata\SetVelocity(velocity)
        movedata\SetOrigin(pos)
        return true

    SetupMove: (movedata, cmd) =>
        @isLiftingUp = movedata\KeyDown(IN_JUMP)
        if @isLiftingUp
            movedata\SetButtons(movedata\GetButtons() - IN_JUMP)
        cmd\SetButtons(cmd\GetButtons() - IN_JUMP) if cmd\KeyDown(IN_JUMP)

    FinishMove: (movedata) =>
        nativeEntity = @ent\GetEntity()
        mvPos = movedata\GetOrigin()
        pos = nativeEntity\GetPos()
        rpos = pos
        tryMove = util.TraceHull({
            filter: (ent) ->
                return false if nativeEntity == ent
                return true if not IsValid(ent)
                collision = ent\GetCollisionGroup()
                return false if collision == COLLISION_GROUP_WORLD
                return false if collision == COLLISION_GROUP_DEBRIS
                return false if collision == COLLISION_GROUP_DEBRIS_TRIGGER
                return false if collision == COLLISION_GROUP_WEAPON
                return false if collision == COLLISION_GROUP_PASSABLE_DOOR
                return true
            mins: @obbMins
            maxs: @obbMaxs
            start: rpos
            endpos: mvPos
        })

        velocity = movedata\GetVelocity()
        newVelocity = velocity
        length = velocity\Length()

        if not tryMove.Hit
            nativeEntity\SetPos(mvPos)
        else
            if IsValid(tryMove.Entity)
                newVelocity = Vector(0, 0, 0)
                movedata\SetVelocity(newVelocity)
                newPos = tryMove.HitPos + tryMove.HitNormal
                nativeEntity\SetPos(newPos)
                movedata\SetOrigin(newPos)
            else
                newVelocity = velocity - tryMove.HitNormal * velocity\Dot(tryMove.HitNormal * 1.1)
                movedata\SetVelocity(newVelocity)
                newPos = tryMove.HitPos + tryMove.HitNormal
                nativeEntity\SetPos(newPos)
                movedata\SetOrigin(newPos)
            if length > 7 and SERVER and FLIGHT_DAMAGE\GetBool()
                dmgInfo = DamageInfo()
                dmgInfo\SetAttacker(nativeEntity)
                dmgInfo\SetInflictor(nativeEntity)
                dmgInfo\SetDamageType(DMG_CRUSH)
                calcDamage = math.Clamp((length / 4) ^ 2, 1, 100)
                if calcDamage >= 100
                    nativeEntity\EmitSound('physics/flesh/flesh_bloody_break.wav', 100)
                elseif calcDamage > 70
                    nativeEntity\EmitSound('physics/flesh/flesh_bloody_break.wav', 100)
                elseif calcDamage > 50
                    nativeEntity\EmitSound("physics/body/body_medium_break#{math.random(2, 4)}.wav", 75)
                elseif calcDamage > 25
                    nativeEntity\EmitSound("physics/body/body_medium_impact_hard#{math.random(1, 6)}.wav", 75)
                elseif calcDamage > 10
                    nativeEntity\EmitSound("physics/body/body_medium_impact_soft#{math.random(1, 7)}.wav", 75)
                else
                    nativeEntity\EmitSound("physics/flesh/flesh_impact_bullet#{math.random(1, 5)}.wav", 75)
                dmgInfo\SetDamage(calcDamage)
                nativeEntity\TakeDamageInfo(dmgInfo)
        @lastVelocity = newVelocity

PPM2.PonyflyController = PonyflyController

hook.Add 'SetupMove', 'PPM2.Ponyfly', (movedata, cmd) =>
    return if not @IsPonyCached()
    data = @GetPonyData()
    return if not data
    return if not data\GetFly()
    flight = data\GetFlightController()
    return if not flight
    return flight\SetupMove(movedata, cmd)

hook.Add 'Move', 'PPM2.Ponyfly', (movedata) =>
    return if not @IsPonyCached()
    data = @GetPonyData()
    return if not data
    return if not data\GetFly()
    flight = data\GetFlightController()
    return if not flight
    return flight\Think(movedata)

hook.Add 'FinishMove', 'PPM2.Ponyfly', (movedata) =>
    return if not @IsPonyCached()
    data = @GetPonyData()
    return if not data
    return if not data\GetFly()
    flight = data\GetFlightController()
    return if not flight
    return flight\FinishMove(movedata)

hook.Add 'CalcMainActivity', 'PPM2.Ponyfly', (movedata) =>
    return if not @IsPonyCached()
    data = @GetPonyData()
    return if not data
    return if not data\GetFly()
    return ACT_SWIM, 370

if SERVER
    concommand.Add 'ppm2_fly', =>
        return if not ALLOW_FLIGHT\GetBool()
        return if not IsValid(@)
        return if not @IsPonyCached()
        data = @GetPonyData()
        return if not data
        if data\GetFly()
            return data\SetFly(false) if FORCE_ALLOW_FLIGHT\GetBool()
            can = hook.Run('PlayerNoClip', @, false) or hook.Run('PPM2Fly', @, false)
            data\SetFly(false) if can
        else
            return if data\GetRace() ~= PPM2.RACE_PEGASUS and data\GetRace() ~= PPM2.RACE_ALICORN
            return data\SetFly(true) if FORCE_ALLOW_FLIGHT\GetBool()
            can = hook.Run('PlayerNoClip', @, true) or hook.Run('PPM2Fly', @, true)
            data\SetFly(true) if can
else
    lastDouble = 0
    lastMessage = 0
    lastMessage2 = 0
    hook.Add 'PlayerBindPress', 'PPM2.Ponyfly', (bind = '', pressed = false) =>
        return if not ALLOW_FLIGHT\GetBool()
        return if not pressed
        return if bind ~= '+jump' and bind ~= 'jump'
        if lastDouble > RealTime()
            return if not @IsPonyCached()
            data = @GetPonyData()
            return if not data
            if data\GetRace() ~= PPM2.RACE_PEGASUS and data\GetRace() ~= PPM2.RACE_ALICORN
                if lastMessage < RealTime()
                    lastMessage = RealTime() + 1
                    PPM2.ChatPrint('You need to be a Pegasus or an Alicorn to fly!')
                return
            can = hook.Run('PlayerNoClip', @, not data\GetFly()) or hook.Run('PPM2Fly', @, not data\GetFly())
            if not can
                if lastMessage2 < RealTime()
                    lastMessage2 = RealTime() + 1
                    PPM2.ChatPrint("You can not #{data\GetFly() and 'land' or 'fly'} right now.")
                return
            RunConsoleCommand('ppm2_fly')
            lastDouble = 0
            return
        lastDouble = RealTime() + 0.2

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

ALLOW_FLIGHT = CreateConVar('ppm2_sv_flight', '1', {FCVAR_ARCHIVE, FCVAR_REPLICATED, FCVAR_NOTIFY}, 'Allow flight for pegasus and alicorns. It obeys PlayerNoClip hook.')
FORCE_ALLOW_FLIGHT = CreateConVar('ppm2_sv_flight_force', '0', {FCVAR_ARCHIVE, FCVAR_REPLICATED, FCVAR_NOTIFY}, 'Ignore PlayerNoClip hook')
FLIGHT_DAMAGE = CreateConVar('ppm2_sv_flightdmg', '1', {FCVAR_ARCHIVE, FCVAR_REPLICATED, FCVAR_NOTIFY}, 'Damage players in flight')

class PonyflyController
    new: (data) =>
        @controller = data
        @ent = data.ent
        @speedMult = 0.5
        @speedMultDirections = 1.25
        @dragMult = 0.95
        @obbCenter = Vector(0, 0, 0)
        @obbMins = Vector(0, 0, 0)
        @obbMaxs = Vector(0, 0, 0)
        @speedMultDiv = 2
        @speedMultLift = 2
        @pitch = 0
        @yaw = 0
        @roll = 0
        @angleLerp = 1
        @lastVelocity = Vector(0, 0, 0)
        @lastState = false

    GetEntity: => @ent
    GetData: => @controller
    GetController: => @controller

    Switch: (status = false) =>
        @ent = @controller.ent
        return if not IsValid(@ent)
        return if @lastState == status
        @lastState = status
        if not status
            {:p, :y, :r} = @ent\EyeAngles()
            newAng = Angle(p, y, 0)
            @ent\SetEyeAngles(newAng)
            @ent\SetMoveType(MOVETYPE_WALK)
            @roll = 0
            @pitch = 0
            @yaw = 0
            @ent\SetVelocity(@lastVelocity * 50)
            @lastVelocity = Vector(0, 0, 0)
        else
            @lastVelocity = Vector(0, 0, 0)
            @ent\SetVelocity(-@ent\GetVelocity() * .97)
            @ent\SetMoveType(MOVETYPE_CUSTOM)
            @obbCenter = @ent\OBBCenter()
            @obbMins = @ent\OBBMins()
            @obbMaxs = @ent\OBBMaxs()
            @roll = 0
            @pitch = 0
            @yaw = 0
    Think: (movedata) =>
        pos     = movedata\GetOrigin()
        ang     = movedata\GetAngles()
        fwd     = ang\Forward()
        bcwd    = -ang\Forward()
        right   = ang\Right()
        left    = -ang\Right()
        up      = ang\Up()
        down    = -ang\Up()
        W       = movedata\KeyDown(IN_FORWARD)
        S       = movedata\KeyDown(IN_BACK)
        D       = movedata\KeyDown(IN_MOVERIGHT)
        A       = movedata\KeyDown(IN_MOVELEFT)
        CTRL    = movedata\KeyDown(IN_DUCK)
        MULT    = FrameTime() * 66

        velocity = movedata\GetVelocity()
        cSpeed = velocity\Length()
        cSpeed = 1 if cSpeed < 1
        dragSqrt = math.min(math.sqrt(cSpeed) / cSpeed * 2, 0.99)
        cSpeedMult = @speedMultDiv / cSpeed
        cSpeedMult = @speedMultDiv if cSpeedMult ~= cSpeedMult
        cSpeedLiftMult = @speedMultLift / cSpeed
        cSpeedLiftMult = @speedMultLift if cSpeedLiftMult ~= cSpeedLiftMult

        dragCalc = math.Clamp(@dragMult / dragSqrt, 0, 0.99)
        pitch = 0
        yaw = 0
        roll = 0

        hit = false
        hitLift = false

        if W
            velocity += fwd * MULT * @speedMult * cSpeedMult * @speedMultDirections
            hit = true
            pitch += 20

        if S
            velocity += bcwd * MULT * @speedMult * cSpeedMult * @speedMultDirections
            hit = true
            pitch -= 20

        if A
            velocity += left * MULT * @speedMult * cSpeedMult * @speedMultDirections
            hit = true
            roll -= 20

        if D
            velocity += right * MULT * @speedMult * cSpeedMult * @speedMultDirections
            hit = true
            roll += 20

        if CTRL
            velocity += Vector(0, 0, -MULT * @speedMult * cSpeedLiftMult)
            hitLift = true

        if @isLiftingUp
            velocity += Vector(0, 0, MULT * @speedMult * cSpeedLiftMult)
            hitLift = true

        if CLIENT
            lerpMult = FrameTime() * @angleLerp
            {:p, :y, :r} = ang
            p -= @pitch
            y -= @yaw
            @pitch = Lerp(lerpMult, @pitch, pitch)
            @yaw = Lerp(lerpMult, @yaw, yaw)
            @roll = Lerp(lerpMult, @roll, roll)
            p += @pitch
            y += @yaw
            r = @roll + math.sin(RealTime()) * 2
            newAng = Angle(p, y, r)
            @ent\SetEyeAngles(newAng)

        if not hit
            velocity.x *= dragCalc
            velocity.y *= dragCalc

        if not hitLift
            velocity.z *= dragCalc
            velocity.z += math.sin(RealTime() * 2) * .01

        pos += velocity

        movedata\SetVelocity(velocity)
        movedata\SetOrigin(pos)
        return true

    SetupMove: (movedata, cmd) =>
        @isLiftingUp = movedata\KeyDown(IN_JUMP)
        if @isLiftingUp
            movedata\SetButtons(movedata\GetButtons() - IN_JUMP)
        cmd\SetButtons(cmd\GetButtons() - IN_JUMP) if cmd\KeyDown(IN_JUMP)

    FinishMove: (movedata) =>
        mvPos = movedata\GetOrigin()
        pos = @ent\GetPos()
        rpos = pos
        tryMove = util.TraceHull({
            filter: (ent) ->
                return false if @ent == ent
                return true if not IsValid(ent)
                collision = ent\GetCollisionGroup()
                return false if collision == COLLISION_GROUP_WORLD
                return false if collision == COLLISION_GROUP_DEBRIS
                return false if collision == COLLISION_GROUP_DEBRIS_TRIGGER
                return false if collision == COLLISION_GROUP_WEAPON
                return false if collision == COLLISION_GROUP_PASSABLE_DOOR
                return true
            mins: @obbMins
            maxs: @obbMaxs
            start: rpos
            endpos: mvPos
        })

        velocity = movedata\GetVelocity()
        newVelocity = velocity
        length = velocity\Length()

        if not tryMove.Hit
            @ent\SetPos(mvPos)
        else
            if IsValid(tryMove.Entity)
                newVelocity = Vector(0, 0, 0)
                movedata\SetVelocity(newVelocity)
                newPos = tryMove.HitPos + tryMove.HitNormal
                @ent\SetPos(newPos)
                movedata\SetOrigin(newPos)
            else
                newVelocity = velocity - tryMove.HitNormal * velocity\Dot(tryMove.HitNormal * 1.1)
                movedata\SetVelocity(newVelocity)
                newPos = tryMove.HitPos + tryMove.HitNormal
                @ent\SetPos(newPos)
                movedata\SetOrigin(newPos)
            if length > 7 and SERVER and FLIGHT_DAMAGE\GetBool()
                dmgInfo = DamageInfo()
                dmgInfo\SetAttacker(@ent)
                dmgInfo\SetInflictor(@ent)
                dmgInfo\SetDamageType(DMG_CRUSH)
                calcDamage = math.Clamp((length / 4) ^ 2, 1, 100)
                if calcDamage >= 100
                    @ent\EmitSound('physics/flesh/flesh_bloody_break.wav', 100)
                elseif calcDamage > 70
                    @ent\EmitSound('physics/flesh/flesh_bloody_break.wav', 100)
                elseif calcDamage > 50
                    @ent\EmitSound("physics/body/body_medium_break#{math.random(2, 4)}.wav", 75)
                elseif calcDamage > 25
                    @ent\EmitSound("physics/body/body_medium_impact_hard#{math.random(1, 6)}.wav", 75)
                elseif calcDamage > 10
                    @ent\EmitSound("physics/body/body_medium_impact_soft#{math.random(1, 7)}.wav", 75)
                else
                    @ent\EmitSound("physics/flesh/flesh_impact_bullet#{math.random(1, 5)}.wav", 75)
                dmgInfo\SetDamage(calcDamage)
                @ent\TakeDamageInfo(dmgInfo)
        @lastVelocity = newVelocity

PPM2.PonyflyController = PonyflyController

hook.Add 'SetupMove', 'PPM2.Ponyfly', (movedata, cmd) =>
    return if not @IsPonyCached()
    data = @GetPonyData()
    return if not data
    return if not data\GetFly()
    flight = data\GetFlightController()
    return if not flight
    return flight\SetupMove(movedata, cmd)

hook.Add 'Move', 'PPM2.Ponyfly', (movedata) =>
    return if not @IsPonyCached()
    data = @GetPonyData()
    return if not data
    return if not data\GetFly()
    flight = data\GetFlightController()
    return if not flight
    return flight\Think(movedata)

hook.Add 'FinishMove', 'PPM2.Ponyfly', (movedata) =>
    return if not @IsPonyCached()
    data = @GetPonyData()
    return if not data
    return if not data\GetFly()
    flight = data\GetFlightController()
    return if not flight
    return flight\FinishMove(movedata)

hook.Add 'CalcMainActivity', 'PPM2.Ponyfly', (movedata) =>
    return if not @IsPonyCached()
    data = @GetPonyData()
    return if not data
    return if not data\GetFly()
    return ACT_SWIM, 370

if SERVER
    concommand.Add 'ppm2_fly', =>
        return if not ALLOW_FLIGHT\GetBool()
        return if not IsValid(@)
        return if not @IsPonyCached()
        data = @GetPonyData()
        return if not data
        if data\GetFly()
            return data\SetFly(false) if FORCE_ALLOW_FLIGHT\GetBool()
            can = hook.Run('PlayerNoClip', @, false) or hook.Run('PPM2Fly', @, false)
            data\SetFly(false) if can
        else
            return if data\GetRace() ~= PPM2.RACE_PEGASUS and data\GetRace() ~= PPM2.RACE_ALICORN
            return data\SetFly(true) if FORCE_ALLOW_FLIGHT\GetBool()
            can = hook.Run('PlayerNoClip', @, true) or hook.Run('PPM2Fly', @, true)
            data\SetFly(true) if can
else
    lastDouble = 0
    lastMessage = 0
    lastMessage2 = 0
    hook.Add 'PlayerBindPress', 'PPM2.Ponyfly', (bind = '', pressed = false) =>
        return if not ALLOW_FLIGHT\GetBool()
        return if not pressed
        return if bind ~= '+jump' and bind ~= 'jump'
        if lastDouble > RealTime()
            return if not @IsPonyCached()
            data = @GetPonyData()
            return if not data
            if data\GetRace() ~= PPM2.RACE_PEGASUS and data\GetRace() ~= PPM2.RACE_ALICORN
                if lastMessage < RealTime()
                    lastMessage = RealTime() + 1
                    PPM2.ChatPrint('You need to be a Pegasus or an Alicorn to fly!')
                return
            can = hook.Run('PlayerNoClip', @, not data\GetFly()) or hook.Run('PPM2Fly', @, not data\GetFly())
            if not can
                if lastMessage2 < RealTime()
                    lastMessage2 = RealTime() + 1
                    PPM2.ChatPrint("You can not #{data\GetFly() and 'land' or 'fly'} right now.")
                return
            RunConsoleCommand('ppm2_fly')
            lastDouble = 0
            return
        lastDouble = RealTime() + 0.2


Implement Screen\needsResize

Implement Screen\needsResize

require "LunoPunk.geometry.Matrix"

export ^

class Screen
	new: =>
		@init!
		-- LP.engine\addChild @__sprite if LP.renderMode == RenderMode.Buffer

	init: =>
		@__x, @__y = 0, 0
		@__originX, @__originY = 0, 0
		@__angle, @__current = 0, 0
		@__scale, @__scaleX, @__scaleY = 1, 1, 1
		@__matrix = Matrix!
		@__update!

	-- bd Bitmap
	disposeBitmap = (bd) =>

	-- Resizes the screen by recreating the bitmap buffer.
	resize: =>

	-- Swaps screen buffers.
	swap: =>

	-- Add a filter.
	--
	-- @param	filter	The filter to add.
	addFilter: (filter) =>

	-- Refreshes the screen.
	refresh: =>

	-- Redraws the screen.
	redraw: =>

	-- @private Re-applies transformation matrix.
	__update: =>
		return if @__matrix == nil -- prevent update on init
		@__matrix.b, @__matrix.c = 0, 0
		@__matrix.a, @__matrix.d = @fullScaleX!, @fullScaleY!
		@__matrix.tx = -@originX! * @__matrix.a
		@__matrix.ty = -@originY! * @__matrix.d
		@__matrix\rotate @__angle if @__angle != 0
		@__matrix.tx += @originX! * @fullScaleX! + @x!
		@__matrix.ty += @originY! * @fullScaleY! + @y!
		-- TODO
		-- _sprite.transform.matrix = _matrix;

	-- Refresh color of the screen.
	color: (value) =>
		return @__color if value == nil or @__color == value
		@__color = value
		@__color

	-- X offset of the screen.
	-- value int
	-- return int
	x: (value) =>
		return @__x if value == nil or @__x == value
		@__x = value
		@__update!
		@__x

	-- Y offset of the screen.
	-- value int
	-- return int
	y: (value) =>
		return @__y if value == nil or @__y == value
		@__y = value
		@__update!
		@__y

	-- X origin of transformations.
	-- value int
	-- return int
	originX: (value) =>
		return @__originX if value == nil or @__originX == value
		@__originX = value
		@__update!
		@__originX

	-- Y origin of transformations.
	-- value int
	-- return int
	originY: (value) =>
		return @__originY if value == nil or @__originY == value
		@__originY = value
		@__update!
		@__originY

	-- X scale of the screen.
	-- value float
	-- return float
	scaleX: (value) =>
		return @__scaleX if value == nil or @__scaleX == value
		@__scaleX = value
		@fullScaleX = => @scaleX! * @scale!
		@__update!
		@needsResize = => true
		@__scaleX

	-- Y scale of the screen.
	-- value float
	-- return float
	scaleY: (value) =>
		return @__scaleY if value == nil or @__scaleY == value
		@__scaleY = value
		@fullScaleY = => @scaleY! * @scale!
		@__update!
		@needsResize = => true
		@__scaleY

	-- Scale factor of the screen. Final scale is scaleX * scale by scaleY * scale, so
	-- you can use this factor to scale the screen both horizontally and vertically.
	-- value float
	-- return float
	scale: (value) =>
		return @__scale if value == nil or @__scale == value
		@__scale = value
		@fullScaleX = => @scaleX! * @scale!
		@fullScaleY = => @scaleY! * @scale!
		@__update!
		@needsResize = => true
		@__scale

	-- Final X scale value of the screen
	fullScaleX: => 1

	-- Final Y scale value of the screen
	fullScaleY: => 1

	-- True if the scale of the screen has changed.
	needsResize: => LP.width != @width! or LP.height != @height!

	-- Rotation of the screen, in degrees.
	angle: (value) =>
		return @__angle * LP.DEG if value == nil
		return value if @__angle == value * LP.RAD
		@__angle = value * LP.RAD
		@__update!
		@__angle

	-- Whether screen smoothing should be used or not.
	-- value bool
	-- return bool
	smoothing = (value) =>
		-- TODO
		@__smoothing = value if value != nil
		@__smoothing

	-- Width of the screen.
	width: => love.graphics.getWidth!

	-- Height of the screen.
	height: => love.graphics.getHeight!

	-- X position of the mouse on the screen.
	mouseX: => love.mouse.getX!

	-- Y position of the mouse on the screen.
	mouseY: => love.mouse.getY!

	-- Captures the current screen as an Image object.
	-- @return	A new Image object.
	capture: =>
	-- TODO return LunoPunk.graphics.Image

require "LunoPunk.geometry.Matrix"

export ^

class Screen
	new: =>
		@init!
		-- LP.engine\addChild @__sprite if LP.renderMode == RenderMode.Buffer

	init: =>
		@__x, @__y = 0, 0
		@__originX, @__originY = 0, 0
		@__angle, @__current = 0, 0
		@__scale, @__scaleX, @__scaleY = 1, 1, 1
		@__matrix = Matrix!
		@__update!

	-- bd Bitmap
	disposeBitmap = (bd) =>

	-- Resizes the screen by recreating the bitmap buffer.
	resize: =>

	-- Swaps screen buffers.
	swap: =>

	-- Add a filter.
	--
	-- @param	filter	The filter to add.
	addFilter: (filter) =>

	-- Refreshes the screen.
	refresh: =>

	-- Redraws the screen.
	redraw: =>

	-- @private Re-applies transformation matrix.
	__update: =>
		return if @__matrix == nil -- prevent update on init
		@__matrix.b, @__matrix.c = 0, 0
		@__matrix.a, @__matrix.d = @fullScaleX!, @fullScaleY!
		@__matrix.tx = -@originX! * @__matrix.a
		@__matrix.ty = -@originY! * @__matrix.d
		@__matrix\rotate @__angle if @__angle != 0
		@__matrix.tx += @originX! * @fullScaleX! + @x!
		@__matrix.ty += @originY! * @fullScaleY! + @y!
		-- TODO
		-- _sprite.transform.matrix = _matrix;

	-- Refresh color of the screen.
	color: (value) =>
		return @__color if value == nil or @__color == value
		@__color = value
		@__color

	-- X offset of the screen.
	-- value int
	-- return int
	x: (value) =>
		return @__x if value == nil or @__x == value
		@__x = value
		@__update!
		@__x

	-- Y offset of the screen.
	-- value int
	-- return int
	y: (value) =>
		return @__y if value == nil or @__y == value
		@__y = value
		@__update!
		@__y

	-- X origin of transformations.
	-- value int
	-- return int
	originX: (value) =>
		return @__originX if value == nil or @__originX == value
		@__originX = value
		@__update!
		@__originX

	-- Y origin of transformations.
	-- value int
	-- return int
	originY: (value) =>
		return @__originY if value == nil or @__originY == value
		@__originY = value
		@__update!
		@__originY

	-- X scale of the screen.
	-- value float
	-- return float
	scaleX: (value) =>
		return @__scaleX if value == nil or @__scaleX == value
		@__scaleX = value
		@fullScaleX = => @scaleX! * @scale!
		@__update!
		@needsResize = => true
		@__scaleX

	-- Y scale of the screen.
	-- value float
	-- return float
	scaleY: (value) =>
		return @__scaleY if value == nil or @__scaleY == value
		@__scaleY = value
		@fullScaleY = => @scaleY! * @scale!
		@__update!
		@needsResize = => true
		@__scaleY

	-- Scale factor of the screen. Final scale is scaleX * scale by scaleY * scale, so
	-- you can use this factor to scale the screen both horizontally and vertically.
	-- value float
	-- return float
	scale: (value) =>
		return @__scale if value == nil or @__scale == value
		@__scale = value
		@fullScaleX = => @scaleX! * @scale!
		@fullScaleY = => @scaleY! * @scale!
		@__update!
		@needsResize = => true
		@__scale

	-- Final X scale value of the screen
	fullScaleX: => 1

	-- Final Y scale value of the screen
	fullScaleY: => 1

	-- True if the scale of the screen has changed.
	needsResize: => false

	-- Rotation of the screen, in degrees.
	angle: (value) =>
		return @__angle * LP.DEG if value == nil
		return value if @__angle == value * LP.RAD
		@__angle = value * LP.RAD
		@__update!
		@__angle

	-- Whether screen smoothing should be used or not.
	-- value bool
	-- return bool
	smoothing = (value) =>
		-- TODO
		@__smoothing = value if value != nil
		@__smoothing

	-- Width of the screen.
	width: => love.graphics.getWidth!

	-- Height of the screen.
	height: => love.graphics.getHeight!

	-- X position of the mouse on the screen.
	mouseX: => love.mouse.getX!

	-- Y position of the mouse on the screen.
	mouseY: => love.mouse.getY!

	-- Captures the current screen as an Image object.
	-- @return	A new Image object.
	capture: =>
	-- TODO return LunoPunk.graphics.Image


add heap action

add heap action

log = require 'log'

class SysCtrl
  info: (req, res) ->
    res\send "{name: #{wifi.sta.gethostname()}}"

  reset: (req, res) ->
    res.client\on 'disconnection', node.restart
    res\send ""

  heap: (req, res) ->
    res\send "{heap: #{node.heap()}}"

return {
  SysCtrl: SysCtrl()
}

log = require 'log'

class SysCtrl
  info: (req, res) ->
    res\send "{name: #{wifi.sta.gethostname()}}"

  reset: (req, res) ->
    res.client\on 'disconnection', node.restart
    res\send ""

return {
  SysCtrl: SysCtrl()
}


require controller module as global var

require controller module as global var

log = require "log"
ctls = require "controller"

class Router
  new: (@routes = {}) =>
    return

  _process: (req, res) =>
    matched = @routes["#{req.method} #{req.url}"]
    if matched != nil
      ctls[matched.controller][matched.action](req, res)

  process: (req, res) =>
    @_process req, res

return Router

log = require "log"

class Router
  new: (@routes = {}) =>
    return

  _process: (req, res) =>
    matched = @routes["#{req.method} #{req.url}"]
    if matched != nil
      ctls = require "controller"
      ctls[matched.controller][matched.action](req, res)

  process: (req, res) =>
    @_process req, res

return Router


indentation

indentation

import Model, enum from require "lapis.db.model"
import create_table, types, add_column, drop_column from require "lapis.db.schema"
Users = require "lazuli.modules.user_management.models.users"

class Profiles extends Model
  @getOrCreateByUser: (user)=>
    if type(user)=="number"
      user=Users\find user
    return nil, "user not found" unless user
    (@find{user_id: user.id}) or (@create{user_id: user.id})

  @relations: {
    {"user",            belongs_to: "Users"}
    {"acl_gender",      belongs_to: "ACLs"}
    {"acl_birthday_y",  belongs_to: "ACLs"}
    {"acl_birthday_dm", belongs_to: "ACLs"}
    {"acl_about",       belongs_to: "ACLs"}
  }

  @get_relation_model: (name)=> switch name
    when "Users"
      require "lazuli.modules.user_management.models.users"
    when "ACLs"
      require "models.acls"

  @migrations: {
    ->
      create_table "profiles", {
        {"id", types.serial}
        {"user_id", types.integer}
        "PRIMARY KEY (id)"
      }
    ->
      add_column  "profiles", "birthday",             types.date null: true
      add_column  "profiles", "about",                types.text null: true
    ->
      add_column  "profiles", "privacy_birthday_dm",  types.boolean
      add_column  "profiles", "privacy_birthday_y",   types.boolean
      add_column  "profiles", "privacy_birthday_age", types.boolean default: true
    ->
      add_column  "profiles", "gender",               types.varchar null: true
      add_column  "profiles", "privacy_gender",       types.boolean
    ->
      drop_column "profiles", "privacy_gender"
      drop_column "profiles", "privacy_birthday_age"
      drop_column "profiles", "privacy_birthday_y"
      drop_column "profiles", "privacy_birthday_dm"
      add_column  "profiles", "acl_gender_id",        types.integer null: true
      add_column  "profiles", "acl_birthday_dm_id",   types.integer null: true
      add_column  "profiles", "acl_birthday_y_id",    types.integer null: true
    ->
      add_column  "profiles", "acl_about_id",    types.integer null: true
  }