Fix color formatting issue.

Fix color formatting issue.

-- Copyright (c) 2014 by Adam Hellberg <sharparam@sharparam.com>.
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in
-- all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-- THE SOFTWARE.

NAME, T = ...

{:GetRealmName, :LARGE_NUMBER_SEPERATOR} = _G

T.string =
    split: (str, delim using nil) ->
        return nil unless str
        t = {}
        if delim then
            return {str} unless str\find delim
            pattern = '(.-)' .. delim .. '()'
            local lastpos
            for part, pos in str\gmatch pattern
                t[#t + 1] = part
                lastpos = pos
            t[#t + 1] = str\sub lastpos
        else
            for token in str\gmatch '[^%s]+'
                t[#t + 1] = token
        t

T.number =
    -- Format function courtesy of SDPhantom from WoWInterface
    -- http://www.wowinterface.com/forums/showpost.php?p=270917&postcount=11
    format: (number using LARGE_NUMBER_SEPERATOR) ->
        number = tostring number
        number\gsub '^(%d)(%d+)', (pre, post) ->
            pre .. post\reverse!\gsub('(%d%d%d)', '%1' .. LARGE_NUMBER_SEPERATOR)\reverse!

local equal

T.table =
    equal: (first, second using equal) ->
        first_type = type first
        second_type = type second
        return false if first_type != 'table' or second_type != 'table'
        for k, v in pairs first
            if type(k) == 'table'
                error 'Comparison with tables as keys not yet implemented'
            if type(v) == 'table'
                return false unless equal v, second[k]
            else
                return false if v != second[k]
        true

import equal from T.table

T.misc =
    fix_name: (name, realm using GetRealmName) ->
        dashindex = name\find '-'
        return name if dashindex and dashindex != name\len!
        return name .. '-' .. realm\gsub '%s', '' if realm
        name = name\sub 1, dashindex - 1 if dashindex
        name .. '-' .. GetRealmName!\gsub '%s', ''

    decorate: (text, color using nil) ->
        '|cff%s%s|r'\format color, text

    -- This basically just calls loadstring on a text
    -- representation of a Lua object like a number,
    -- string or table
    deserialize: (str) ->
        func = assert loadstring 'return ' .. str
        setfenv func, {} -- Don't give access to anything
        pcall func

-- Copyright (c) 2014 by Adam Hellberg <sharparam@sharparam.com>.
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in
-- all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-- THE SOFTWARE.

NAME, T = ...

{:GetRealmName, :LARGE_NUMBER_SEPERATOR} = _G

T.string =
    split: (str, delim using nil) ->
        return nil unless str
        t = {}
        if delim then
            return {str} unless str\find delim
            pattern = '(.-)' .. delim .. '()'
            local lastpos
            for part, pos in str\gmatch pattern
                t[#t + 1] = part
                lastpos = pos
            t[#t + 1] = str\sub lastpos
        else
            for token in str\gmatch '[^%s]+'
                t[#t + 1] = token
        t

T.number =
    -- Format function courtesy of SDPhantom from WoWInterface
    -- http://www.wowinterface.com/forums/showpost.php?p=270917&postcount=11
    format: (number using LARGE_NUMBER_SEPERATOR) ->
        number = tostring number
        number\gsub '^(%d)(%d+)', (pre, post) ->
            pre .. post\reverse!\gsub('(%d%d%d)', '%1' .. LARGE_NUMBER_SEPERATOR)\reverse!

local equal

T.table =
    equal: (first, second using equal) ->
        first_type = type first
        second_type = type second
        return false if first_type != 'table' or second_type != 'table'
        for k, v in pairs first
            if type(k) == 'table'
                error 'Comparison with tables as keys not yet implemented'
            if type(v) == 'table'
                return false unless equal v, second[k]
            else
                return false if v != second[k]
        true

import equal from T.table

T.misc =
    fix_name: (name, realm using GetRealmName) ->
        dashindex = name\find '-'
        return name if dashindex and dashindex != name\len!
        return name .. '-' .. realm\gsub '%s', '' if realm
        name = name\sub 1, dashindex - 1 if dashindex
        name .. '-' .. GetRealmName!\gsub '%s', ''

    decorate: (text, color using nil) ->
        '\127cff%s%s\127r'\format color, text

    -- This basically just calls loadstring on a text
    -- representation of a Lua object like a number,
    -- string or table
    deserialize: (str) ->
        func = assert loadstring 'return ' .. str
        setfenv func, {} -- Don't give access to anything
        pcall func


Fixed typo

Fixed typo

--
-- 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.MIN_WEIGHT = 0.7
PPM2.MAX_WEIGHT = 1.5

PPM2.MIN_SCALE = 0.5
PPM2.MAX_SCALE = 1.3

PPM2.PONY_HEIGHT_MODIFIER = 0.64
PPM2.PONY_HEIGHT_MODIFIER_DUCK = 1.12
PPM2.PONY_HEIGHT_MODIFIER_DUCK_HULL = 1

PPM2.MIN_NECK = 0.6
PPM2.MAX_NECK = 1.4

PPM2.MIN_LEGS = 0.6
PPM2.MAX_LEGS = 1.75

PPM2.PONY_JUMP_MODIFIER = 1.4

PPM2.PLAYER_VOFFSET = 64 * PPM2.PONY_HEIGHT_MODIFIER
PPM2.PLAYER_VOFFSET_DUCK = 28 * PPM2.PONY_HEIGHT_MODIFIER_DUCK

PPM2.PLAYER_VIEW_OFFSET = Vector(0, 0, PPM2.PLAYER_VOFFSET)
PPM2.PLAYER_VIEW_OFFSET_DUCK = Vector(0, 0, PPM2.PLAYER_VOFFSET_DUCK)

PPM2.PLAYER_VIEW_OFFSET_ORIGINAL = Vector(0, 0, 64)
PPM2.PLAYER_VIEW_OFFSET_DUCK_ORIGINAL = Vector(0, 0, 28)

PPM2.MIN_TAIL_SIZE = 0.6
PPM2.MAX_TAIL_SIZE = 1.7 -- i luv big tails

PPM2.MIN_IRIS = 0.4
PPM2.MAX_IRIS = 1.3

PPM2.MIN_HOLE = 0.1
PPM2.MAX_HOLE = .95

PPM2.MIN_HOLE_SHIFT = -0.5
PPM2.MAX_HOLE_SHIFT = 0.5

PPM2.MIN_PUPIL_SIZE = 0.2
PPM2.MAX_PUPIL_SIZE = 1

PPM2.MIN_EYE_ROTATION = -180
PPM2.MAX_EYE_ROTATION = 180

PPM2.AvaliableTails = {
    'MAILCALL'
    'FLOOFEH'
    'ADVENTUROUS'
    'SHOWBOAT'
    'ASSERTIVE'
    'BOLD'
    'STUMPY'
    'SPEEDSTER'
    'EDGY'
    'RADICAL'
    'BOOKWORM'
    'BUMPKIN'
    'POOFEH'
    'CURLY'
    'NONE'
}

PPM2.AvaliableUpperManes = {
    'MAILCALL', 'FLOOFEH', 'ADVENTUROUS', 'SHOWBOAT', 'ASSERTIVE'
    'BOLD', 'STUMPY', 'SPEEDSTER', 'RADICAL', 'SPIKED'
    'BOOKWORM', 'BUMPKIN', 'POOFEH', 'CURLY', 'INSTRUCTOR', 'NONE'
}

PPM2.AvaliableLowerManes = {
    'MAILCALL', 'FLOOFEH', 'ADVENTUROUS', 'SHOWBOAT'
    'ASSERTIVE', 'BOLD', 'STUMPY', 'HIPPIE', 'SPEEDSTER'
    'BOOKWORM', 'BUMPKIN', 'CURLY', 'NONE'
}

PPM2.EyelashTypes = {
    'Default', 'Double', 'Coy', 'Full', 'Mess', 'None'
}

PPM2.BodyDetails = {
    'None', 'Leg gradient', 'Lines', 'Stripes', 'Head stripes'
    'Freckles', 'Hooves big', 'Hooves small', 'Head layer'
    'Hooves big rnd', 'Hooves small rnd', 'Spots 1', 'Robotic'
    'DASH-E', 'Eye Scar', 'Eye Wound', 'Scars', 'MGS Socks'
    'Sharp Hooves', 'Sharp Hooves 2', 'Muzzle', 'Eye Scar Left'
    'Eye Scar Right'
}

PPM2.BodyDetailsEnum = {
    'NONE', 'GRADIENT', 'LINES', 'STRIPES', 'HSTRIPES'
    'FRECKLES', 'HOOF_BIG', 'HOOF_SMALL', 'LAYER'
    'HOOF_BIG_ROUND', 'HOOF_SMALL_ROUND', 'SPOTS', 'ROBOTIC'
    'DASH_E', 'EYE_SCAR', 'EYE_WOUND', 'SCARS', 'MGS_SOCKS'
    'SHARP_HOOVES', 'SHARP_HOOVES_2', 'MUZZLE', 'EYE_SCAR_LEFT'
    'EYE_SCAR_RIGHT'
}

PPM2.SocksTypes = {
    'DEFAULT'
    'GEOMETRIC1'
    'GEOMETRIC2'
    'GEOMETRIC3'
    'GEOMETRIC4'
    'GEOMETRIC5'
    'GEOMETRIC6'
    'GEOMETRIC7'
    'GEOMETRIC8'
    'DARK1'
    'FLOWERS10'
    'FLOWERS11'
    'FLOWERS12'
    'FLOWERS13'
    'FLOWERS14'
    'FLOWERS15'
    'FLOWERS16'
    'FLOWERS17'
    'FLOWERS18'
    'FLOWERS19'
    'FLOWERS2'
    'FLOWERS20'
    'FLOWERS3'
    'FLOWERS4'
    'FLOWERS5'
    'FLOWERS6'
    'FLOWERS7'
    'FLOWERS8'
    'FLOWERS9'
    'GREY1'
    'GREY2'
    'GREY3'
    'HEARTS1'
    'HEARTS2'
    'SNOW1'
    'WALLPAPER1'
    'WALLPAPER2'
    'WALLPAPER3'
}

PPM2.AvaliableLightwarps = {
    'SFM_PONY'
    'AIRBRUSH'
    'HARD_LIGHT'
    'PURPLE_SKY'
    'SPAWN'
    'TF2'
    'TF2_CINEMATIC'
    'TF2_CLASSIC'
    'WELL_OILED'
}

PPM2.MAX_LIGHTWARP = #PPM2.AvaliableLightwarps - 1

PPM2.AvaliableLightwarpsPaths = ['models/ppm/lightwarps/' .. mat\lower() for mat in *PPM2.AvaliableLightwarps]

PPM2.DefaultCutiemarks = {
    '8ball', 'dice', 'magichat',
    'magichat02', 'record', 'microphone',
    'bits', 'checkered', 'lumps',
    'mirror', 'camera', 'magnifier',
    'padlock', 'binaryfile', 'floppydisk',
    'cube', 'bulb', 'battery',
    'deskfan', 'flames', 'alarm',
    'myon', 'beer', 'berryglass',
    'roadsign', 'greentree', 'seasons',
    'palette', 'palette02', 'palette03',
    'lightningstone', 'partiallycloudy', 'thunderstorm',
    'storm', 'stoppedwatch', 'twistedclock',
    'surfboard', 'surfboard02', 'star',
    'ussr', 'vault', 'anarchy',
    'suit', 'deathscythe', 'shoop',
    'smiley', 'dawsome', 'weegee'
    'applej', 'applem', 'bon_bon', 'carrots', 'celestia', 'cloudy', 'custom01', 'custom02', 'derpy', 'firezap',
    'fluttershy', 'fruits', 'island', 'lyra', 'mine', 'note', 'octavia', 'pankk', 'pinkie_pie', 'rainbow_dash',
    'rarity', 'rosen', 'sflowers', 'storm', 'time', 'time2', 'trixie', 'twilight', 'waters', 'weer', 'zecora'
}

PPM2.AvaliableUpperManesNew = {
    'MAILCALL', 'FLOOFEH', 'ADVENTUROUS', 'SHOWBOAT', 'ASSERTIVE'
    'BOLD', 'STUMPY', 'SPEEDSTER', 'RADICAL', 'SPIKED'
    'BOOKWORM', 'BUMPKIN', 'POOFEH', 'CURLY', 'INSTRUCTOR', 'NONE'
}

PPM2.AvaliableLowerManesNew = {
    'MAILCALL', 'FLOOFEH', 'ADVENTUROUS', 'SHOWBOAT'
    'ASSERTIVE', 'BOLD', 'STUMPY', 'HIPPIE', 'SPEEDSTER'
    'BOOKWORM', 'BUMPKIN', 'CURLY', 'NONE'
}

PPM2.AvaliableTailsNew = {
    'MAILCALL', 'FLOOFEH', 'ADVENTUROUS', 'SHOWBOAT'
    'ASSERTIVE', 'BOLD', 'STUMPY', 'SPEEDSTER', 'EDGY'
    'RADICAL', 'BOOKWORM', 'BUMPKIN', 'POOFEH', 'CURLY', 'NONE'
}

PPM2.AvaliableEyeTypes = {
    'DEFAULT', 'APERTURE'
}

PPM2.AvaliablePonyWings = {'DEFAULT', 'BATPONY'}

PPM2.AvaliablePonySuits = {'NONE', 'ROYAL_GUARD', 'SHADOWBOLTS_FULL', 'SHADOWBOLTS_LIGHT', 'WONDERBOLTS_FULL', 'WONDERBOLTS_LIGHT', 'SPIDERMANE_LIGHT', 'SPIDERMANE_FULL'}

do
    i = -1
    for mark in *PPM2.DefaultCutiemarks
        i += 1
        PPM2["CMARK_#{mark\upper()}"] = i

PPM2.MIN_EYELASHES = 0
PPM2.MAX_EYELASHES = #PPM2.EyelashTypes - 1
PPM2.EYELASHES_NONE = #PPM2.EyelashTypes - 1

PPM2.MIN_TAILS = 0
PPM2.MAX_TAILS = #PPM2.AvaliableTails - 1

PPM2.MIN_TAILS_NEW = 0
PPM2.MAX_TAILS_NEW = #PPM2.AvaliableTailsNew - 1

PPM2.MIN_UPPER_MANES = 0
PPM2.MAX_UPPER_MANES = #PPM2.AvaliableUpperManes - 1

PPM2.MIN_LOWER_MANES = 0
PPM2.MAX_LOWER_MANES = #PPM2.AvaliableLowerManes - 1

PPM2.MIN_UPPER_MANES_NEW = 0
PPM2.MAX_UPPER_MANES_NEW = #PPM2.AvaliableUpperManesNew - 1

PPM2.MIN_LOWER_MANES_NEW = 0
PPM2.MAX_LOWER_MANES_NEW = #PPM2.AvaliableLowerManesNew - 1

PPM2.MIN_EYE_TYPE = 0
PPM2.MAX_EYE_TYPE = #PPM2.AvaliableEyeTypes - 1

PPM2.MIN_DETAIL = 0
PPM2.MAX_DETAIL = #PPM2.BodyDetails - 1

PPM2.MIN_CMARK = 0
PPM2.MAX_CMARK = #PPM2.DefaultCutiemarks - 1

PPM2.MIN_SUIT = 0
PPM2.MAX_SUIT = #PPM2.AvaliablePonySuits - 1

PPM2.MIN_WINGS = 0
PPM2.MAX_WINGS = #PPM2.AvaliablePonyWings - 1

PPM2.MIN_SOCKS = 0
PPM2.MAX_SOCKS = #PPM2.SocksTypes - 1

PPM2.GENDER_FEMALE = 0
PPM2.GENDER_MALE = 1

PPM2.MAX_BODY_DETAILS = 8

PPM2.RACE_EARTH = 0
PPM2.RACE_PEGASUS = 1
PPM2.RACE_UNICORN = 2
PPM2.RACE_ALICORN = 3
PPM2.RACE_ENUMS = {'EARTH', 'PEGASUS', 'UNICORN', 'ALICORN'}

PPM2.AGE_FILLY = 0
PPM2.AGE_ADULT = 1
PPM2.AGE_MATURE = 2
PPM2.AGE_ENUMS = {'FILLY', 'ADULT', 'MATURE'}

PPM2.MIN_DERP_STRENGTH = 0.1
PPM2.MAX_DERP_STRENGTH = 1.3

PPM2.MIN_MALE_BUFF = 0
PPM2.DEFAULT_MALE_BUFF = 1
PPM2.MAX_MALE_BUFF = 2

PPM2.MIN_WING = 0.1
PPM2.MIN_WINGX = -10
PPM2.MIN_WINGY = -10
PPM2.MIN_WINGZ = -10
PPM2.MAX_WING = 2
PPM2.MAX_WINGX = 10
PPM2.MAX_WINGY = 10
PPM2.MAX_WINGZ = 10

--
-- 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.MIN_WEIGHT = 0.7
PPM2.MAX_WEIGHT = 1.5

PPM2.MIN_SCALE = 0.5
PPM2.MAX_SCALE = 1.3

PPM2.PONY_HEIGHT_MODIFIER = 0.64
PPM2.PONY_HEIGHT_MODIFIER_DUCK = 1.12
PPM2.PONY_HEIGHT_MODIFIER_DUCK_HULL = 1

PPM2.MIN_NECK = 0.6
PPM2.MAX_NECK = 1.4

PPM2.MIN_LEGS = 0.6
PPM2.MAX_LEGS = 1.75

PPM2.PONY_JUMP_MODIFIER = 1.4

PPM2.PLAYER_VOFFSET = 64 * PPM2.PONY_HEIGHT_MODIFIER
PPM2.PLAYER_VOFFSET_DUCK = 28 * PPM2.PONY_HEIGHT_MODIFIER_DUCK

PPM2.PLAYER_VIEW_OFFSET = Vector(0, 0, PPM2.PLAYER_VOFFSET)
PPM2.PLAYER_VIEW_OFFSET_DUCK = Vector(0, 0, PPM2.PLAYER_VOFFSET_DUCK)

PPM2.PLAYER_VIEW_OFFSET_ORIGINAL = Vector(0, 0, 64)
PPM2.PLAYER_VIEW_OFFSET_DUCK_ORIGINAL = Vector(0, 0, 28)

PPM2.MIN_TAIL_SIZE = 0.6
PPM2.MAX_TAIL_SIZE = 1.7 -- i luv big tails

PPM2.MIN_IRIS = 0.4
PPM2.MAX_IRIS = 1.3

PPM2.MIN_HOLE = 0.1
PPM2.MAX_HOLE = .95

PPM2.MIN_HOLE_SHIFT = -0.5
PPM2.MAX_HOLE_SHIFT = 0.5

PPM2.MIN_PUPIL_SIZE = 0.2
PPM2.MAX_PUPIL_SIZE = 1

PPM2.MIN_EYE_ROTATION = -180
PPM2.MAX_EYE_ROTATION = 180

PPM2.AvaliableTails = {
    'MAILCALL'
    'FLOOFEH'
    'ADVENTUROUS'
    'SHOWBOAT'
    'ASSERTIVE'
    'BOLD'
    'STUMPY'
    'SPEEDSTER'
    'EDGY'
    'RADICAL'
    'BOOKWORM'
    'BUMPKIN'
    'POOFEH'
    'CURLY'
    'NONE'
}

PPM2.AvaliableUpperManes = {
    'MAILCALL', 'FLOOFEH', 'ADVENTUROUS', 'SHOWBOAT', 'ASSERTIVE'
    'BOLD', 'STUMPY', 'SPEEDSTER', 'RADICAL', 'SPIKED'
    'BOOKWORM', 'BUMPKIN', 'POOFEH', 'CURLY', 'INSTRUCTOR', 'NONE'
}

PPM2.AvaliableLowerManes = {
    'MAILCALL', 'FLOOFEH', 'ADVENTUROUS', 'SHOWBOAT'
    'ASSERTIVE', 'BOLD', 'STUMPY', 'HIPPIE', 'SPEEDSTER'
    'BOOKWORM', 'BUMPKIN', 'CURLY', 'NONE'
}

PPM2.EyelashTypes = {
    'Default', 'Double', 'Coy', 'Full', 'Mess', 'None'
}

PPM2.BodyDetails = {
    'None', 'Leg gradient', 'Lines', 'Stripes', 'Head stripes'
    'Freckles', 'Hooves big', 'Hooves small', 'Head layer'
    'Hooves big rnd', 'Hooves small rnd', 'Spots 1', 'Robotic'
    'DASH-E', 'Eye Scar', 'Eye Wound', 'Scars', 'MGS Socks'
    'Sharp Hooves', 'Sharp Hooves 2', 'Muzzle', 'Eye Scar Left'
    'Eye Scar Right'
}

PPM2.BodyDetailsEnum = {
    'NONE', 'GRADIENT', 'LINES', 'STRIPES', 'HSTRIPES'
    'FRECKLES', 'HOOF_BIG', 'HOOF_SMALL', 'LAYER'
    'HOOF_BIG_ROUND', 'HOOF_SMALL_ROUND', 'SPOTS', 'ROBOTIC'
    'DASH_E', 'EYE_SCAR', 'EYE_WOUND', 'SCARS', 'MGS_SOCKS'
    'SHARP_HOOVES', 'SHARP_HOOVES_2', 'MUZZLE', 'EYE_SCAR_LEFT'
    'EYE_SCAR_RIGHT'
}

PPM2.SocksTypes = {
    'DEFAULT'
    'GEOMETRIC1'
    'GEOMETRIC2'
    'GEOMETRIC3'
    'GEOMETRIC4'
    'GEOMETRIC5'
    'GEOMETRIC6'
    'GEOMETRIC7'
    'GEOMETRIC8'
    'DARK1'
    'FLOWERS10'
    'FLOWERS11'
    'FLOWERS12'
    'FLOWERS13'
    'FLOWERS14'
    'FLOWERS15'
    'FLOWERS16'
    'FLOWERS17'
    'FLOWERS18'
    'FLOWERS19'
    'FLOWERS2'
    'FLOWERS20'
    'FLOWERS3'
    'FLOWERS4'
    'FLOWERS5'
    'FLOWERS6'
    'FLOWERS7'
    'FLOWERS8'
    'FLOWERS9'
    'GREY1'
    'GREY2'
    'GREY3'
    'HEARTS1'
    'HEARTS2'
    'SNOW1'
    'WALLPAPER1'
    'WALLPAPER2'
    'WALLPAPER3'
}

PPM2.AvaliableLightwarps = {
    'SFM_PONY'
    'AIRBRUSH'
    'HARD_LIGHT'
    'PURPLE_SKY'
    'SPAWN'
    'TF2'
    'TF2_CINEMATIC'
    'TF2_CLASSIC'
    'WELL_OILED'
}

PPM2.MAX_LIGHTWARP = #PPM2.AvaliableLightwarps - 1

PPM2.AvaliableLightwarpsPaths = ['models/ppm/lightwarps/' .. mat\lower() for mat in *PPM2.AvaliableLightwraps]

PPM2.DefaultCutiemarks = {
    '8ball', 'dice', 'magichat',
    'magichat02', 'record', 'microphone',
    'bits', 'checkered', 'lumps',
    'mirror', 'camera', 'magnifier',
    'padlock', 'binaryfile', 'floppydisk',
    'cube', 'bulb', 'battery',
    'deskfan', 'flames', 'alarm',
    'myon', 'beer', 'berryglass',
    'roadsign', 'greentree', 'seasons',
    'palette', 'palette02', 'palette03',
    'lightningstone', 'partiallycloudy', 'thunderstorm',
    'storm', 'stoppedwatch', 'twistedclock',
    'surfboard', 'surfboard02', 'star',
    'ussr', 'vault', 'anarchy',
    'suit', 'deathscythe', 'shoop',
    'smiley', 'dawsome', 'weegee'
    'applej', 'applem', 'bon_bon', 'carrots', 'celestia', 'cloudy', 'custom01', 'custom02', 'derpy', 'firezap',
    'fluttershy', 'fruits', 'island', 'lyra', 'mine', 'note', 'octavia', 'pankk', 'pinkie_pie', 'rainbow_dash',
    'rarity', 'rosen', 'sflowers', 'storm', 'time', 'time2', 'trixie', 'twilight', 'waters', 'weer', 'zecora'
}

PPM2.AvaliableUpperManesNew = {
    'MAILCALL', 'FLOOFEH', 'ADVENTUROUS', 'SHOWBOAT', 'ASSERTIVE'
    'BOLD', 'STUMPY', 'SPEEDSTER', 'RADICAL', 'SPIKED'
    'BOOKWORM', 'BUMPKIN', 'POOFEH', 'CURLY', 'INSTRUCTOR', 'NONE'
}

PPM2.AvaliableLowerManesNew = {
    'MAILCALL', 'FLOOFEH', 'ADVENTUROUS', 'SHOWBOAT'
    'ASSERTIVE', 'BOLD', 'STUMPY', 'HIPPIE', 'SPEEDSTER'
    'BOOKWORM', 'BUMPKIN', 'CURLY', 'NONE'
}

PPM2.AvaliableTailsNew = {
    'MAILCALL', 'FLOOFEH', 'ADVENTUROUS', 'SHOWBOAT'
    'ASSERTIVE', 'BOLD', 'STUMPY', 'SPEEDSTER', 'EDGY'
    'RADICAL', 'BOOKWORM', 'BUMPKIN', 'POOFEH', 'CURLY', 'NONE'
}

PPM2.AvaliableEyeTypes = {
    'DEFAULT', 'APERTURE'
}

PPM2.AvaliablePonyWings = {'DEFAULT', 'BATPONY'}

PPM2.AvaliablePonySuits = {'NONE', 'ROYAL_GUARD', 'SHADOWBOLTS_FULL', 'SHADOWBOLTS_LIGHT', 'WONDERBOLTS_FULL', 'WONDERBOLTS_LIGHT', 'SPIDERMANE_LIGHT', 'SPIDERMANE_FULL'}

do
    i = -1
    for mark in *PPM2.DefaultCutiemarks
        i += 1
        PPM2["CMARK_#{mark\upper()}"] = i

PPM2.MIN_EYELASHES = 0
PPM2.MAX_EYELASHES = #PPM2.EyelashTypes - 1
PPM2.EYELASHES_NONE = #PPM2.EyelashTypes - 1

PPM2.MIN_TAILS = 0
PPM2.MAX_TAILS = #PPM2.AvaliableTails - 1

PPM2.MIN_TAILS_NEW = 0
PPM2.MAX_TAILS_NEW = #PPM2.AvaliableTailsNew - 1

PPM2.MIN_UPPER_MANES = 0
PPM2.MAX_UPPER_MANES = #PPM2.AvaliableUpperManes - 1

PPM2.MIN_LOWER_MANES = 0
PPM2.MAX_LOWER_MANES = #PPM2.AvaliableLowerManes - 1

PPM2.MIN_UPPER_MANES_NEW = 0
PPM2.MAX_UPPER_MANES_NEW = #PPM2.AvaliableUpperManesNew - 1

PPM2.MIN_LOWER_MANES_NEW = 0
PPM2.MAX_LOWER_MANES_NEW = #PPM2.AvaliableLowerManesNew - 1

PPM2.MIN_EYE_TYPE = 0
PPM2.MAX_EYE_TYPE = #PPM2.AvaliableEyeTypes - 1

PPM2.MIN_DETAIL = 0
PPM2.MAX_DETAIL = #PPM2.BodyDetails - 1

PPM2.MIN_CMARK = 0
PPM2.MAX_CMARK = #PPM2.DefaultCutiemarks - 1

PPM2.MIN_SUIT = 0
PPM2.MAX_SUIT = #PPM2.AvaliablePonySuits - 1

PPM2.MIN_WINGS = 0
PPM2.MAX_WINGS = #PPM2.AvaliablePonyWings - 1

PPM2.MIN_SOCKS = 0
PPM2.MAX_SOCKS = #PPM2.SocksTypes - 1

PPM2.GENDER_FEMALE = 0
PPM2.GENDER_MALE = 1

PPM2.MAX_BODY_DETAILS = 8

PPM2.RACE_EARTH = 0
PPM2.RACE_PEGASUS = 1
PPM2.RACE_UNICORN = 2
PPM2.RACE_ALICORN = 3
PPM2.RACE_ENUMS = {'EARTH', 'PEGASUS', 'UNICORN', 'ALICORN'}

PPM2.AGE_FILLY = 0
PPM2.AGE_ADULT = 1
PPM2.AGE_MATURE = 2
PPM2.AGE_ENUMS = {'FILLY', 'ADULT', 'MATURE'}

PPM2.MIN_DERP_STRENGTH = 0.1
PPM2.MAX_DERP_STRENGTH = 1.3

PPM2.MIN_MALE_BUFF = 0
PPM2.DEFAULT_MALE_BUFF = 1
PPM2.MAX_MALE_BUFF = 2

PPM2.MIN_WING = 0.1
PPM2.MIN_WINGX = -10
PPM2.MIN_WINGY = -10
PPM2.MIN_WINGZ = -10
PPM2.MAX_WING = 2
PPM2.MAX_WINGX = 10
PPM2.MAX_WINGY = 10
PPM2.MAX_WINGZ = 10


Update some of the LP defaults

Update some of the LP defaults

* Also stub gotoIsNull

require "LovePunk.Tweener"

export ^

class lp
	-- Private
	BASELAYER = 10
	DEG = nil
	INT_MAX_VALUE = nil
	NUMBER_MAX_VALUE = nil
	RAD = nil
	VERSION = "0.0.1"

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

	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) ->
	clampInRect: (object, x, y, width, height, padding) ->
	clear: (array) ->
	colorLerp: (fromColor, toColor, t) ->
	consoleEnabled: ->
	convertColor: (color) ->
	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) ->
	getBitmap: (source) ->
	getBlue: (color) ->
	getColorHSV: (h, s, v) ->
	getColorHue: (color) ->
	getColorRGB: (R, G, B) ->
	getColorSaturation: (color) ->
	getColorValue: (color) ->
	getGreen: (color) ->
	getRed: (color) ->

	-- read-only
	DEG: ->
		DEG

	-- read-only
	NUMBER_MAX_VALUE: ->
		NUMBER_MAX_VALUE

	-- read-only
	RAD: ->
		RAD

	-- read-only
	VERSION: ->
		VERSION

	gotoIsNull: ->
		-- TODO STUB
		false

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

	rand: (amount) ->
		math.random(amount)

	randomizeSeed: ->
		math.randomseed os.time()

	resetCamera: ->
	resize: (width, height) ->
	rotateAround: (object, anchor, angle, relative) ->
	round: (num, precision) ->
	scale: (value, min, max, min2, max2) ->
	scaleClamp: (value, min, max, min2, max2) ->
	setCamera: (x, y) ->
	shuffle: (a) ->
	sign: (value) ->
	stepTowards: (object, x, y, distance) ->
	swap: (current, a, b) ->
	swapScene: ->
	timeFlag: ->
	tween: (object, values, duration, options) ->

LP = lp!

export ^

class lp
	-- Private
	BASELAYER = 10
	DEG = nil
	INT_MAX_VALUE = nil
	NUMBER_MAX_VALUE = nil
	RAD = nil
	VERSION = "0.0.1"

	_gameTime: nil
	_renderTime: nil
	_systemTime: nil
	_updateTime: nil
	assignedFrameRate: nil
	blackColor: 0x00000000
	bounds: nil
	buffer: nil
	camera: nil
	console: nil
	defaultFont: "default"
	elapsed: nil
	engine: nil
	entity: nil
	fixed: nil
	focused: nil
	frameRate: 0
	fullscreen: nil
	halfHeight: nil
	halfWidth: nil
	height: nil
	log: nil
	matrix: nil
	orientations: nil
	pan: nil
	point: nil
	point2: nil
	random: -> math.random!
	randomSeed: nil
	rate: 0
	rect: nil
	renderMode: nil
	scene: nil
	screen: nil
	sprite: nil
	stage: nil
	tweener: nil
	volume: nil
	watch: nil
	width: nil
	windowHeight: nil
	windowWidth: nil
	world: nil
	zero: nil

	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) ->
	clampInRect: (object, x, y, width, height, padding) ->
	clear: (array) ->
	colorLerp: (fromColor, toColor, t) ->
	consoleEnabled: ->
	convertColor: (color) ->
	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) ->
	getBitmap: (source) ->
	getBlue: (color) ->
	getColorHSV: (h, s, v) ->
	getColorHue: (color) ->
	getColorRGB: (R, G, B) ->
	getColorSaturation: (color) ->
	getColorValue: (color) ->
	getGreen: (color) ->
	getRed: (color) ->

	-- read-only
	DEG: ->
		DEG

	-- read-only
	NUMBER_MAX_VALUE: ->
		NUMBER_MAX_VALUE

	-- read-only
	RAD: ->
		RAD

	-- read-only
	VERSION: ->
		VERSION

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

	rand: (amount) ->
		math.random(amount)

	randomizeSeed: ->
		math.randomseed os.time()

	resetCamera: ->
	resize: (width, height) ->
	rotateAround: (object, anchor, angle, relative) ->
	round: (num, precision) ->
	scale: (value, min, max, min2, max2) ->
	scaleClamp: (value, min, max, min2, max2) ->
	setCamera: (x, y) ->
	shuffle: (a) ->
	sign: (value) ->
	stepTowards: (object, x, y, distance) ->
	swap: (current, a, b) ->
	swapScene: ->
	timeFlag: ->
	tween: (object, values, duration, options) ->

LP = lp!


Added render priority to draw in

Added render priority to draw in

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

TASK_RENDER_TYPE = CreateConVar('ppm2_task_render_type', '1', {FCVAR_ARCHIVE, FCVAR_NOTIFY}, 'Task rendering type (e.g. pony ragdolls and NPCs). 1 - better render; less conflicts; more FPS. 0 - "old-style" render; possible conflicts;')
DRAW_LEGS_DEPTH = CreateConVar('ppm2_render_legsdepth', '1', {FCVAR_ARCHIVE, FCVAR_NOTIFY}, 'Render legs in depth pass. Useful with Boken DoF enabled')
LEGS_RENDER_TYPE = CreateConVar('ppm2_render_legstype', '0', {FCVAR_ARCHIVE, FCVAR_NOTIFY}, 'When render legs. 0 - Before Opaque renderables; 1 - after Translucent renderables')
ENABLE_NEW_RAGDOLLS = CreateConVar('ppm2_sv_new_ragdolls', '1', {FCVAR_ARCHIVE, FCVAR_NOTIFY, FCVAR_REPLICATED}, 'Enable new ragdolls')

timer.Create 'PPM2.ModelChecks', 1, 0, ->
    for task in *PPM2.NetworkedPonyData.RenderTasks
        ent = task.ent
        if IsValid(ent)
            ent.__cachedIsPony = ent\IsPony()

    for ply in *player.GetAll()
        ply.__cachedIsPony = ply\IsPony()

        if ply.__cachedIsPony
            for wep in *ply\GetWeapons()
                continue if not wep
                wep\SetNoDraw(true)
                wep.__ppm2_weapon_hit = true
        else
            for wep in *ply\GetWeapons()
                continue if not wep
                continue if not wep.__ppm2_weapon_hit
                wep\SetNoDraw(false)
                ply.__ppm2_weapon_hit = false

PPM2.PreDrawOpaqueRenderables = (bDrawingDepth, bDrawingSkybox) ->
    if not LEGS_RENDER_TYPE\GetBool()
        with LocalPlayer()
            if .__cachedIsPony and \Alive()
                if data = \GetPonyData()
                    data\GetRenderController()\DrawLegs()

PPM2.PostDrawOpaqueRenderables = (bDrawingDepth, bDrawingSkybox) ->
    if bDrawingDepth and DRAW_LEGS_DEPTH\GetBool()
        with LocalPlayer()
            if .__cachedIsPony and \Alive()
                if data = \GetPonyData()
                    data\GetRenderController()\DrawLegsDepth()

    return if bDrawingDepth or bDrawingSkybox

    for task in *PPM2.NetworkedPonyData.RenderTasks
        ent = task.ent
        if IsValid(ent)
            if ent.__cachedIsPony
                if not TASK_RENDER_TYPE\GetBool()
                    ent\SetNoDraw(true)
                    ent.__ppm2_task_hit = true
                    renderController = task\GetRenderController()
                    renderController\PreDraw(ent)
                    ent\DrawModel()
                    renderController\PostDraw(ent)
                else
                    if ent.__ppm2_task_hit
                        ent.__ppm2_task_hit = false
                        ent\SetNoDraw(false)
                    renderController = task\GetRenderController()
                    renderController\PreDraw(ent)
                    renderController\PostDraw(ent)
            else
                if ent.__ppm2_task_hit
                    ent.__ppm2_task_hit = false
                    ent\SetNoDraw(false)
                    task\Reset()

    if not ENABLE_NEW_RAGDOLLS\GetBool()
        for ply in *player.GetAll()
            alive = ply\Alive()
            ply.__ppm2_last_dead = RealTime() + 2 if not alive
            if ply.__cachedIsPony
                if ply\GetPonyData() and not alive
                    data = ply\GetPonyData()
                    rag = ply\GetRagdollEntity()
                    if IsValid(rag)
                        renderController = data\GetRenderController()
                        data\DoRagdollMerge()
                        if renderController
                            renderController\PreDraw(rag)
                            rag\DrawModel()
                            renderController\PostDraw(rag)

    if LEGS_RENDER_TYPE\GetBool()
        with LocalPlayer()
            if .__cachedIsPony and \Alive()
                if data = \GetPonyData()
                    data\GetRenderController()\DrawLegs()

PPM2.PrePlayerDraw = =>
    return unless @GetPonyData()
    @__cachedIsPony = @IsPony()
    return if not @__cachedIsPony
    return if @__ppm2_last_draw == FrameNumber()
    @__ppm2_last_draw = FrameNumber()
    return if @IsDormant()
    @__ppm2_last_dead = @__ppm2_last_dead or 0
    return if @__ppm2_last_dead > RealTime()
    data = @GetPonyData()
    renderController = data\GetRenderController()
    status = renderController\PreDraw() if renderController
PPM2.PostPlayerDraw = =>
    return unless @GetPonyData()
    return unless @__cachedIsPony
    data = @GetPonyData()
    renderController = data\GetRenderController()
    renderController\PostDraw() if renderController

hook.Add 'PrePlayerDraw', 'PPM2.PlayerDraw', PPM2.PrePlayerDraw, 2
hook.Add 'PostPlayerDraw', 'PPM2.PostPlayerDraw', PPM2.PostPlayerDraw, 2
hook.Add 'PostDrawOpaqueRenderables', 'PPM2.PostDrawOpaqueRenderables', PPM2.PostDrawOpaqueRenderables, 2
hook.Add 'PreDrawOpaqueRenderables', 'PPM2.PreDrawOpaqueRenderables', PPM2.PreDrawOpaqueRenderables, 2

SHOULD_DRAW_VIEWMODEL = CreateConVar('cl_ppm2_draw_hands', '1', {FCVAR_ARCHIVE}, 'Should draw hooves as viewmodel')

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

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

PlayerRespawn = ->
    ent = net.ReadEntity()
    return if not IsValid(ent)
    return if not ent\GetPonyData()
    ent\GetPonyData()\PlayerRespawn()

PlayerDeath = ->
    ent = net.ReadEntity()
    return if not IsValid(ent)
    return if not ent\GetPonyData()
    ent\GetPonyData()\PlayerDeath()

lastDataSend = 0
lastDataReceived = 0
net.Receive 'PPM2.RequestPonyData', ->
    return if lastDataReceived > RealTime()
    lastDataReceived = RealTime() + 10
    RunConsoleCommand('ppm2_reload')

net.Receive 'PPM2.PlayerRespawn', PlayerRespawn
net.Receive 'PPM2.PlayerDeath', PlayerDeath

concommand.Add 'ppm2_require', ->
    net.Start('PPM2.Require')
    net.SendToServer()
    PPM2.Message 'Requesting pony data...'

concommand.Add 'ppm2_reload', ->
    return if lastDataSend > RealTime()
    lastDataSend = RealTime() + 10
    instance = PPM2.GetMainData()
    newData = instance\CreateNetworkObject()
    newData\Create()
    instance\SetNetworkData(newData)
    PPM2.Message 'Sending pony data to server...'

hook.Add 'KeyPress', 'PPM2.RequireData', ->
    hook.Remove 'KeyPress', 'PPM2.RequireData'
    RunConsoleCommand('ppm2_reload')
    timer.Simple 3, -> RunConsoleCommand('ppm2_require')

PPM_HINT_COLOR_FIRST = Color(255, 255, 255)
PPM_HINT_COLOR_SECOND = Color(0, 0, 0)

hook.Add 'HUDPaint', 'PPM2.EditorStatus', ->
    lply = LocalPlayer()
    lpos = lply\EyePos()
    for ply in *player.GetAll()
        if ply ~= lply
            if ply\GetNWBool('PPM2.InEditor')
                pos = ply\EyePos()
                dist = pos\Distance(lpos)
                if dist < 250
                    pos.z += 10
                    alpha = math.Clamp(1 - dist / 250, 0.1, 1)
                    {:x, :y} = pos\ToScreen()
                    draw.DrawText('In PPM/2 Editor', 'HudHintTextLarge', x + 1, y + 1, PPM_HINT_COLOR_SECOND, TEXT_ALIGN_CENTER)
                    draw.DrawText('In PPM/2 Editor', 'HudHintTextLarge', x, y, PPM_HINT_COLOR_FIRST, TEXT_ALIGN_CENTER)

concommand.Add 'ppm2_cleanup', ->
    for ent in *ents.GetAll()
        if ent.isPonyPropModel and not IsValid(ent.manePlayer)
            ent\Remove()
    PPM2.Message('All unused models were removed')

timer.Create 'PPM2.ModelCleanup', 60, 0, ->
    for ent in *ents.GetAll()
        if ent.isPonyPropModel and not IsValid(ent.manePlayer)
            ent\Remove()

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

TASK_RENDER_TYPE = CreateConVar('ppm2_task_render_type', '1', {FCVAR_ARCHIVE, FCVAR_NOTIFY}, 'Task rendering type (e.g. pony ragdolls and NPCs). 1 - better render; less conflicts; more FPS. 0 - "old-style" render; possible conflicts;')

timer.Create 'PPM2.ModelChecks', 1, 0, ->
    for task in *PPM2.NetworkedPonyData.RenderTasks
        ent = task.ent
        if IsValid(ent)
            ent.__cachedIsPony = ent\IsPony()

    for ply in *player.GetAll()
        ply.__cachedIsPony = ply\IsPony()

        if ply.__cachedIsPony
            for wep in *ply\GetWeapons()
                continue if not wep
                wep\SetNoDraw(true)
                wep.__ppm2_weapon_hit = true
        else
            for wep in *ply\GetWeapons()
                continue if not wep
                continue if not wep.__ppm2_weapon_hit
                wep\SetNoDraw(false)
                ply.__ppm2_weapon_hit = false

ENABLE_NEW_RAGDOLLS = CreateConVar('ppm2_sv_new_ragdolls', '1', {FCVAR_ARCHIVE, FCVAR_NOTIFY, FCVAR_REPLICATED}, 'Enable new ragdolls')

PPM2.PostDrawOpaqueRenderables = (bDrawingDepth, bDrawingSkybox) ->
    if bDrawingDepth
        with LocalPlayer()
            if .__cachedIsPony and \Alive()
                if data = \GetPonyData()
                    data\GetRenderController()\DrawLegsDepth()

    return if bDrawingDepth or bDrawingSkybox

    for task in *PPM2.NetworkedPonyData.RenderTasks
        ent = task.ent
        if IsValid(ent)
            if ent.__cachedIsPony
                if not TASK_RENDER_TYPE\GetBool()
                    ent\SetNoDraw(true)
                    ent.__ppm2_task_hit = true
                    renderController = task\GetRenderController()
                    renderController\PreDraw(ent)
                    ent\DrawModel()
                    renderController\PostDraw(ent)
                else
                    if ent.__ppm2_task_hit
                        ent.__ppm2_task_hit = false
                        ent\SetNoDraw(false)
                    renderController = task\GetRenderController()
                    renderController\PreDraw(ent)
                    renderController\PostDraw(ent)
            else
                if ent.__ppm2_task_hit
                    ent.__ppm2_task_hit = false
                    ent\SetNoDraw(false)
                    task\Reset()

    if not ENABLE_NEW_RAGDOLLS\GetBool()
        for ply in *player.GetAll()
            alive = ply\Alive()
            ply.__ppm2_last_dead = RealTime() + 2 if not alive
            if ply.__cachedIsPony
                if ply\GetPonyData() and not alive
                    data = ply\GetPonyData()
                    rag = ply\GetRagdollEntity()
                    if IsValid(rag)
                        renderController = data\GetRenderController()
                        data\DoRagdollMerge()
                        if renderController
                            renderController\PreDraw(rag)
                            rag\DrawModel()
                            renderController\PostDraw(rag)

    with LocalPlayer()
        if .__cachedIsPony and \Alive()
            if data = \GetPonyData()
                data\GetRenderController()\DrawLegs()

PPM2.PrePlayerDraw = =>
    return unless @GetPonyData()
    @__cachedIsPony = @IsPony()
    return if not @__cachedIsPony
    return if @__ppm2_last_draw == FrameNumber()
    @__ppm2_last_draw = FrameNumber()
    return if @IsDormant()
    @__ppm2_last_dead = @__ppm2_last_dead or 0
    return if @__ppm2_last_dead > RealTime()
    data = @GetPonyData()
    renderController = data\GetRenderController()
    status = renderController\PreDraw() if renderController
PPM2.PostPlayerDraw = =>
    return unless @GetPonyData()
    return unless @__cachedIsPony
    data = @GetPonyData()
    renderController = data\GetRenderController()
    renderController\PostDraw() if renderController

hook.Add 'PrePlayerDraw', 'PPM2.PlayerDraw', PPM2.PrePlayerDraw, 2
hook.Add 'PostPlayerDraw', 'PPM2.PostPlayerDraw', PPM2.PostPlayerDraw, 2
hook.Add 'PostDrawOpaqueRenderables', 'PPM2.PostDrawOpaqueRenderables', PPM2.PostDrawOpaqueRenderables, 2

SHOULD_DRAW_VIEWMODEL = CreateConVar('cl_ppm2_draw_hands', '1', {FCVAR_ARCHIVE}, 'Should draw hooves as viewmodel')

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

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

PlayerRespawn = ->
    ent = net.ReadEntity()
    return if not IsValid(ent)
    return if not ent\GetPonyData()
    ent\GetPonyData()\PlayerRespawn()

PlayerDeath = ->
    ent = net.ReadEntity()
    return if not IsValid(ent)
    return if not ent\GetPonyData()
    ent\GetPonyData()\PlayerDeath()

lastDataSend = 0
lastDataReceived = 0
net.Receive 'PPM2.RequestPonyData', ->
    return if lastDataReceived > RealTime()
    lastDataReceived = RealTime() + 10
    RunConsoleCommand('ppm2_reload')

net.Receive 'PPM2.PlayerRespawn', PlayerRespawn
net.Receive 'PPM2.PlayerDeath', PlayerDeath

concommand.Add 'ppm2_require', ->
    net.Start('PPM2.Require')
    net.SendToServer()
    PPM2.Message 'Requesting pony data...'

concommand.Add 'ppm2_reload', ->
    return if lastDataSend > RealTime()
    lastDataSend = RealTime() + 10
    instance = PPM2.GetMainData()
    newData = instance\CreateNetworkObject()
    newData\Create()
    instance\SetNetworkData(newData)
    PPM2.Message 'Sending pony data to server...'

hook.Add 'KeyPress', 'PPM2.RequireData', ->
    hook.Remove 'KeyPress', 'PPM2.RequireData'
    RunConsoleCommand('ppm2_reload')
    timer.Simple 3, -> RunConsoleCommand('ppm2_require')

PPM_HINT_COLOR_FIRST = Color(255, 255, 255)
PPM_HINT_COLOR_SECOND = Color(0, 0, 0)

hook.Add 'HUDPaint', 'PPM2.EditorStatus', ->
    lply = LocalPlayer()
    lpos = lply\EyePos()
    for ply in *player.GetAll()
        if ply ~= lply
            if ply\GetNWBool('PPM2.InEditor')
                pos = ply\EyePos()
                dist = pos\Distance(lpos)
                if dist < 250
                    pos.z += 10
                    alpha = math.Clamp(1 - dist / 250, 0.1, 1)
                    {:x, :y} = pos\ToScreen()
                    draw.DrawText('In PPM/2 Editor', 'HudHintTextLarge', x + 1, y + 1, PPM_HINT_COLOR_SECOND, TEXT_ALIGN_CENTER)
                    draw.DrawText('In PPM/2 Editor', 'HudHintTextLarge', x, y, PPM_HINT_COLOR_FIRST, TEXT_ALIGN_CENTER)

concommand.Add 'ppm2_cleanup', ->
    for ent in *ents.GetAll()
        if ent.isPonyPropModel and not IsValid(ent.manePlayer)
            ent\Remove()
    PPM2.Message('All unused models were removed')

timer.Create 'PPM2.ModelCleanup', 60, 0, ->
    for ent in *ents.GetAll()
        if ent.isPonyPropModel and not IsValid(ent.manePlayer)
            ent\Remove()


improved acl entries

improved acl entries

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

class ACL_Entries extends Model

  @relations: {
    {"acl", belongs_to: "ACLs"}
    --{"target_user", belongs_to: "Users"}
    --{"target_include", belongs_to: "ACLs"}
  }

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

  @target_types: enum {
    include: 1
    include_inverted: 2
    user: 3
  }

  @special_targets: enum {
    include_everyone: -1
    include_logged_in: -2
  }

  @migrations: {
    ->
      create_table "acl_entries", {
        {"id",                types.serial}
        {"acl_id",            types.integer}
        {"position",          types.integer default: 0}
        {"policy",            types.boolean}
        {"target_type",       types.integer}
        {"target_id",    types.integer null: true}
        "PRIMARY KEY (id)"
      }
  }

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

class ACL_Entries extends Model

  @relations: {
    {"acl", belongs_to: "ACLs"}
    {"target_user", belongs_to: "Users"}
    {"target_include", belongs_to: "ACLs"}
  }

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

  @target_types: enum {
    include: 1
    user: 2
  }

  @migrations: {
    ->
      create_table "acl_entries", {
        {"id",                types.serial}
        {"acl_id",            types.integer}
        {"position",          types.integer default: 0}
        {"policy",            types.boolean}
        {"target_type",       types.integer}
        {"target_user_id",    types.integer null: true}
        {"target_include_id", types.integer null: true}
        "PRIMARY KEY (id)"
      }
  }


Aegisub library: don't export script_ properties.

Forgot to remove these when converting from macro to module.

-- This library is unlicensed under CC0

libraryVersion = 0x000101

ffi  = require( 'ffi' )

ffi.cdef( [[
typedef struct {
	int x, y;
	unsigned int w, h;
} ASSI_Rect;

uint32_t    assi_getVersion( void );
const char* assi_getErrorString( void* );
void*       assi_init( int, int, const char*, const char* );
int         assi_setHeader( void*, const char* );
int         assi_setScript( void*, const char* );
int         assi_calculateBounds( void*, ASSI_Rect*, const int32_t*, const uint32_t );
void        assi_cleanup( void* );
]] )

-- Figure out a better way to load this?
extension = ('OSX' == ffi.os and '.dylib' or 'Windows' == ffi.os and '.dll' or '.so')
libraryPath = "?user/automation/include/ASSInspector"
success, ASSInspector = pcall( ffi.load, aegisub.decode_path( libraryPath .. "/libASSInspector" .. extension ) )
if not success
	libraryPath = "?data/automation/include/ASSInspector"
	success, ASSInspector = pcall( ffi.load, aegisub.decode_path( libraryPath .. "/libASSInspector" .. extension ) )
	if not success
		error( "Could not load required libASSInspector library." )

-- Inspector is not actually a property of the class but a property of
-- the script. Since we don't need to worry about concurrency or
-- anything silly like that, we can initialize ASSInspector once and
-- keep that reference alive until Aegisub is closed or scripts are all
-- reloaded.
state = {
	inspector: nil
	fontDir:   nil
	resX:      nil
	resY:      nil
}

collectHeader = ( subtitles ) =>
	scriptHeader = {
		"[Script Info]"
	}

	-- These are the only header fields that actually affect the way ASS
	-- is rendered. I don't actually know if ScriptType matters.
	infoFields = {
		PlayResX:   true
		PlayResY:   true
		WrapStyle:  true
		ScriptType: true
		ScaledBorderAndShadow: true
	}

	styles = { }

	seenStyles = false
	resX = 640
	resY = 480

	-- This loop assumes the subtitle script is nicely organized in the
	-- order [Script Info] -> [V4+ Styles] -> [Events]. This isn't
	-- guaranteed for scripts floating around in the wild, but fortunately
	-- Aegisub 3.2 seems to do a very good job of ensuring this order when
	-- a script is loaded. In other words, this loop is probably only safe
	-- to do from within automation.
	for index = 1, #subtitles
		with line = subtitles[index]
			if "info" == .class
				if infoFields[.key]
					table.insert( scriptHeader, .raw )

				if "PlayResX" == .key
					resX = tonumber( .value )
				elseif "PlayResY" == .key
					resY = tonumber( .value )

			elseif "style" == .class
				unless seenStyles
					table.insert( scriptHeader, "[V4+ Styles]\n" )
					seenStyles = true

				styles[.name] = .raw

			elseif "dialogue" == .class
				break

	-- If a video is loaded, use its resolution instead of the script's
	-- resolution. Don't use low-resolution workraws for typesetting!
	vidResX, vidResY = aegisub.video_size( )
	@resX = vidResX or resX
	@resY = vidResY or resY

	@header = table.concat( scriptHeader, '\n' )
	@styles = styles

initializeInspector = ( fontDirectory = state.fontDir ) =>
	if nil == state.inspector
		libVersion = ASSInspector.assi_getVersion!
		if libVersion < libraryVersion
			return nil, "ASSInspector C library is out of date."
		elseif libVersion > libraryVersion
			return nil, "ASSInspector moonscript library is out of date."

		state.inspector = ffi.gc( ASSInspector.assi_init( @resX, @resY, aegisub.decode_path( libraryPath .. "/fonts.conf" ), fontDirectory ), ASSInspector.assi_cleanup )

		if nil == state.inspector
			return nil, "ASSInspector library initialization failed."

		state.resX = @resX
		state.resY = @resY
		state.fontDir = fontDirectory

	return true

validateRect = ( rect ) ->
	bounds = {
		x: tonumber( rect.x )
		y: tonumber( rect.y )
		w: tonumber( rect.w )
		h: tonumber( rect.h )
	}

	if bounds.w == 0 or bounds.h == 0
		return false
	else
		return bounds

defaultTimes = ( lines ) ->
	ffms = aegisub.frame_from_ms
	msff = aegisub.ms_from_frame

	seenTimes = { }
	times = { }
	hasFrames = ffms( 0 )

	for line in *lines
		with line
			if (true == .assi_exhaustive) and hasFrames
				for frame = ffms( .start_time ), ffms( .end_time ) - 1
					frameTime = math.floor( 0.5*( msff( frame ) + msff( frame + 1 ) ) )
					unless seenTimes[frameTime]
						table.insert( times, frameTime )
						seenTimes[frameTime] = true
			else
				unless seenTimes[.start_time]
					table.insert( times, frameTime )
					seenTimes[.start_time] = true

	-- This will only happen if all lines are displayed for 0 frames.
	unless 0 < #times
		return false
	else
		return times

addStyles = ( line, scriptText, seenStyles ) =>
	if @styles[line.style] and not seenStyles[line.style]
		table.insert( scriptText, @styles[line.style] )
		seenStyles[line.style] = true

	line.text\gsub "{(.-)}", ( tagBlock ) ->
		tagBlock\gsub "\\r([^\\}]*)", ( styleName ) ->
			if 0 < #styleName and @styles[styleName] and not seenStyles[styleName]
				table.insert( scriptText, @styles[styleName] )
				seenStyles[styleName] = true

class Inspector

	new: ( subtitles, fontDirectory = aegisub.decode_path( '?script/fonts' ) ) =>
		if '?script/fonts' == fontDirectory
			fontDirectory = nil

		collectHeader( @, subtitles )
		success, message = initializeInspector( @, fontDirectory )
		if nil == success
			return success, message

		@updateHeader!

	updateHeader: ( subtitles ) =>
		if nil != subtitles
			collectHeader( @, subtitles )

		if @resX != state.resX or @resY != state.resY
			@forceLibraryReload!

		if 0 < ASSInspector.assi_setHeader( state.inspector, @header )
			return nil, "Failed to set header.\n" .. ffi.string( ASSInspector.assi_getErrorString( state.inspector ) )

	forceLibraryReload: ( fontDirectory ) =>
		state.inspector = nil
		initializeInspector( @, fontDirectory )

	-- Arguments:
	-- subtitles: the subtitles object that Aegisub passes to the macro.

	-- line: an array-like table of line tables that have at least the
	--       fields `start_time`, `text`, `end_time` and `raw`. `raw` is
	--       mandatory in all cases. `start_time` and `end_time` are only
	--       necessary if a table of times to render the line is not
	--       provided and a video is loaded. Allows rendering 1 or more
	--       lines simultaneously.

	-- times: a table of times (in milliseconds) to render the line at.
	--        Defaults to the start time of the line unless there is a video
	--        loaded and the line contains the field line.assi_exhaustive =
	--        true, in which case it defaults to every single frame that the
	--        line is displayed.

	-- Returns:
	-- An array-like table of bounding boxes, and an array-like table of
	-- render times, the two tables are the same length. If multiple lines
	-- are passed, the bounding box for a given time is the combined
	-- bounding boxes of all the lines.

	-- Error Handling:
	-- If an error is encounted, getBounds returns nil and an error string,
	-- which is typical for lua error reporting. In order to distinguish
	-- between an error and a valid false return, users should make sure
	-- they actually compare result to nil and false. Rather than just
	-- checking that the result is not falsy.

	getBounds: ( lines, times = defaultTimes( lines ) ) =>
		unless times
			return false

		scriptText = { }
		seenStyles = { }
		if @styles.Default
			seenStyles.Default = true
			table.insert( scriptText, @styles.Default )

		for line in *lines
			addStyles( @, line, scriptText, seenStyles )

		table.insert( scriptText, '[Events]' )

		for line in *lines
			table.insert( scriptText, line.raw )

		if 0 < ASSInspector.assi_setScript( state.inspector, table.concat( scriptText, '\n' ) )
			return nil, "Could not set script" .. ffi.string( ASSInspector.assi_getErrorString( state.inspector ) )

		renderCount = #times
		cTimes = ffi.new( 'int32_t[?]', renderCount )
		cRects = ffi.new( 'ASSI_Rect[?]', renderCount )

		for i = 0, renderCount - 1
			cTimes[i] = times[i + 1]

		if 0 < ASSInspector.assi_calculateBounds( state.inspector, cRects, cTimes, renderCount )
			return nil, "Error calculating bounds" .. ffi.string( ASSInspector.assi_getErrorString( state.inspector ) )

		rects = { }
		for i = 0, renderCount - 1
			table.insert( rects, validateRect( cRects[i] ) )

		return rects, times

return Inspector

-- This library is unlicensed under CC0

export script_name        = "ASSInspector Example"
export script_description = "Calculates bounding rectangles on the start and end times of selected events."
export script_author      = "torque"
export script_version     = 0x000101

ffi  = require( 'ffi' )

ffi.cdef( [[
typedef struct {
	int x, y;
	unsigned int w, h;
} ASSI_Rect;

uint32_t    assi_getVersion( void );
const char* assi_getErrorString( void* );
void*       assi_init( int, int, const char*, const char* );
int         assi_setHeader( void*, const char* );
int         assi_setScript( void*, const char* );
int         assi_calculateBounds( void*, ASSI_Rect*, const int32_t*, const uint32_t );
void        assi_cleanup( void* );
]] )

-- Figure out a better way to load this?
extension = ('OSX' == ffi.os and '.dylib' or 'Windows' == ffi.os and '.dll' or '.so')
libraryPath = "?user/automation/include/ASSInspector"
success, ASSInspector = pcall( ffi.load, aegisub.decode_path( libraryPath .. "/libASSInspector" .. extension ) )
if not success
	libraryPath = "?data/automation/include/ASSInspector"
	success, ASSInspector = pcall( ffi.load, aegisub.decode_path( libraryPath .. "/libASSInspector" .. extension ) )
	if not success
		error( "Could not load required libASSInspector library." )

-- Inspector is not actually a property of the class but a property of
-- the script. Since we don't need to worry about concurrency or
-- anything silly like that, we can initialize ASSInspector once and
-- keep that reference alive until Aegisub is closed or scripts are all
-- reloaded.
state = {
	inspector: nil
	fontDir:   nil
	resX:      nil
	resY:      nil
}

collectHeader = ( subtitles ) =>
	scriptHeader = {
		"[Script Info]"
	}

	-- These are the only header fields that actually affect the way ASS
	-- is rendered. I don't actually know if ScriptType matters.
	infoFields = {
		PlayResX:   true
		PlayResY:   true
		WrapStyle:  true
		ScriptType: true
		ScaledBorderAndShadow: true
	}

	styles = { }

	seenStyles = false
	resX = 640
	resY = 480

	-- This loop assumes the subtitle script is nicely organized in the
	-- order [Script Info] -> [V4+ Styles] -> [Events]. This isn't
	-- guaranteed for scripts floating around in the wild, but fortunately
	-- Aegisub 3.2 seems to do a very good job of ensuring this order when
	-- a script is loaded. In other words, this loop is probably only safe
	-- to do from within automation.
	for index = 1, #subtitles
		with line = subtitles[index]
			if "info" == .class
				if infoFields[.key]
					table.insert( scriptHeader, .raw )

				if "PlayResX" == .key
					resX = tonumber( .value )
				elseif "PlayResY" == .key
					resY = tonumber( .value )

			elseif "style" == .class
				unless seenStyles
					table.insert( scriptHeader, "[V4+ Styles]\n" )
					seenStyles = true

				styles[.name] = .raw

			elseif "dialogue" == .class
				break

	-- If a video is loaded, use its resolution instead of the script's
	-- resolution. Don't use low-resolution workraws for typesetting!
	vidResX, vidResY = aegisub.video_size( )
	@resX = vidResX or resX
	@resY = vidResY or resY

	@header = table.concat( scriptHeader, '\n' )
	@styles = styles

initializeInspector = ( fontDirectory = state.fontDir ) =>
	if nil == state.inspector
		libVersion = ASSInspector.assi_getVersion!
		if libVersion < script_version
			return nil, "ASSInspector C library is out of date."
		elseif libVersion > script_version
			return nil, "ASSInspector moonscript library is out of date."

		state.inspector = ffi.gc( ASSInspector.assi_init( @resX, @resY, aegisub.decode_path( libraryPath .. "/fonts.conf" ), fontDirectory ), ASSInspector.assi_cleanup )

		if nil == state.inspector
			return nil, "ASSInspector library initialization failed."

		state.resX = @resX
		state.resY = @resY
		state.fontDir = fontDirectory

	return true

validateRect = ( rect ) ->
	bounds = {
		x: tonumber( rect.x )
		y: tonumber( rect.y )
		w: tonumber( rect.w )
		h: tonumber( rect.h )
	}

	if bounds.w == 0 or bounds.h == 0
		return false
	else
		return bounds

defaultTimes = ( lines ) ->
	ffms = aegisub.frame_from_ms
	msff = aegisub.ms_from_frame

	seenTimes = { }
	times = { }
	hasFrames = ffms( 0 )

	for line in *lines
		with line
			if (true == .assi_exhaustive) and hasFrames
				for frame = ffms( .start_time ), ffms( .end_time ) - 1
					frameTime = math.floor( 0.5*( msff( frame ) + msff( frame + 1 ) ) )
					unless seenTimes[frameTime]
						table.insert( times, frameTime )
						seenTimes[frameTime] = true
			else
				unless seenTimes[.start_time]
					table.insert( times, frameTime )
					seenTimes[.start_time] = true

	-- This will only happen if all lines are displayed for 0 frames.
	unless 0 < #times
		return false
	else
		return times

addStyles = ( line, scriptText, seenStyles ) =>
	if @styles[line.style] and not seenStyles[line.style]
		table.insert( scriptText, @styles[line.style] )
		seenStyles[line.style] = true

	line.text\gsub "{(.-)}", ( tagBlock ) ->
		tagBlock\gsub "\\r([^\\}]*)", ( styleName ) ->
			if 0 < #styleName and @styles[styleName] and not seenStyles[styleName]
				table.insert( scriptText, @styles[styleName] )
				seenStyles[styleName] = true

class Inspector

	new: ( subtitles, fontDirectory = aegisub.decode_path( '?script/fonts' ) ) =>
		if '?script/fonts' == fontDirectory
			fontDirectory = nil

		collectHeader( @, subtitles )
		success, message = initializeInspector( @, fontDirectory )
		if nil == success
			return success, message

		@updateHeader!

	updateHeader: ( subtitles ) =>
		if nil != subtitles
			collectHeader( @, subtitles )

		if @resX != state.resX or @resY != state.resY
			@forceLibraryReload!

		if 0 < ASSInspector.assi_setHeader( state.inspector, @header )
			return nil, "Failed to set header.\n" .. ffi.string( ASSInspector.assi_getErrorString( state.inspector ) )

	forceLibraryReload: ( fontDirectory ) =>
		state.inspector = nil
		initializeInspector( @, fontDirectory )

	-- Arguments:
	-- subtitles: the subtitles object that Aegisub passes to the macro.

	-- line: an array-like table of line tables that have at least the
	--       fields `start_time`, `text`, `end_time` and `raw`. `raw` is
	--       mandatory in all cases. `start_time` and `end_time` are only
	--       necessary if a table of times to render the line is not
	--       provided and a video is loaded. Allows rendering 1 or more
	--       lines simultaneously.

	-- times: a table of times (in milliseconds) to render the line at.
	--        Defaults to the start time of the line unless there is a video
	--        loaded and the line contains the field line.assi_exhaustive =
	--        true, in which case it defaults to every single frame that the
	--        line is displayed.

	-- Returns:
	-- An array-like table of bounding boxes, and an array-like table of
	-- render times, the two tables are the same length. If multiple lines
	-- are passed, the bounding box for a given time is the combined
	-- bounding boxes of all the lines.

	-- Error Handling:
	-- If an error is encounted, getBounds returns nil and an error string,
	-- which is typical for lua error reporting. In order to distinguish
	-- between an error and a valid false return, users should make sure
	-- they actually compare result to nil and false. Rather than just
	-- checking that the result is not falsy.

	getBounds: ( lines, times = defaultTimes( lines ) ) =>
		unless times
			return false

		scriptText = { }
		seenStyles = { }
		if @styles.Default
			seenStyles.Default = true
			table.insert( scriptText, @styles.Default )

		for line in *lines
			addStyles( @, line, scriptText, seenStyles )

		table.insert( scriptText, '[Events]' )

		for line in *lines
			table.insert( scriptText, line.raw )

		if 0 < ASSInspector.assi_setScript( state.inspector, table.concat( scriptText, '\n' ) )
			return nil, "Could not set script" .. ffi.string( ASSInspector.assi_getErrorString( state.inspector ) )

		renderCount = #times
		cTimes = ffi.new( 'int32_t[?]', renderCount )
		cRects = ffi.new( 'ASSI_Rect[?]', renderCount )

		for i = 0, renderCount - 1
			cTimes[i] = times[i + 1]

		if 0 < ASSInspector.assi_calculateBounds( state.inspector, cRects, cTimes, renderCount )
			return nil, "Error calculating bounds" .. ffi.string( ASSInspector.assi_getErrorString( state.inspector ) )

		rects = { }
		for i = 0, renderCount - 1
			table.insert( rects, validateRect( cRects[i] ) )

		return rects, times

return Inspector


Added missing get_filters method.

Added missing get_filters method.

-- This Source Code Form is subject to the terms of the Mozilla Public
-- License, v. 2.0. If a copy of the MPL was not distributed with this
-- file, You can obtain one at http://mozilla.org/MPL/2.0/.
--
-- Copyright 2016 [bk]door.maus

Promise = require "glurg.common.Promise"
BufferFilter = require "glurg.filters.BufferFilter"
ProgramFilter = require "glurg.filters.ProgramFilter"
VertexArrayFilter = require "glurg.filters.VertexArrayFilter"
MeshBlobBuilder = require "glurg.resources.MeshBlobBuilder"

class GL3StaticModelHeuristic
	new: (data) =>
		@array_buffer_filter = BufferFilter("GL_ARRAY_BUFFER")
		@element_array_buffer_filter = BufferFilter("GL_ELEMENT_ARRAY_BUFFER")
		@program_filter = ProgramFilter!
		@vertex_array_filter = VertexArrayFilter!
		@attributes =
			position_binding: data["position"]
			normal_binding: data["normal"]
			texture_coordinate_binding: data["texture"]
		@output_directory = data["output-dir"] or "."

	get_filters: =>
		return @array_buffer_filter, @element_array_buffer_filter, @program_filter, @vertex_array_filter

	make_mesh: (attribute_name) =>
		program = @program_filter\get_current_program!
		attribute_index = program\get_attribute(attribute_name)

		vertex_array = @vertex_array_filter\get_current_vertex_array!
		description = vertex_array.descriptions[attribute_index]

		buffer = @array_buffer_filter\get_buffer(description.array_buffer)

		builder = MeshBlobBuilder!
		extracted_description = builder\extract_attrib_call(description.call)
		extracted_buffer = builder\extracted_buffer_call(buffer.data)

		if extract_description and extracted_buffer
			return Mesh(builder\build!)

	make_model: (positions_mesh, normals_mesh, texture_coordinates_mesh, call) =>
		model = Model(positions_mesh)
		model.normals = normals_mesh
		model.texture_coordinates[1] = texture_coordinates_mesh

		index_format = switch call\get_argument_by_name("type")\query!.value_name
			when 'GL_UNSIGNED_BYTE' then index_format = 'unsigned_byte'
			when 'GL_UNSIGNED_SHORT' then index_format = 'unsigned_short'
			when 'GL_UNSIGNED_INT' then index_format = 'unsigned_integer'
			else error "unknown index type"

		vertex_array = @vertex_array_filter\get_current_vertex_array!
		element_array_buffer = @element_array_buffer_filter\get_buffer(vertex_array.element_array_buffer)
		model\extract_index_data(index_format, element_array_buffer.data)

		return model

	is_compatible: =>
		program = @program_filter\get_current_program!

		vertices = program\has_attribute(@attributes.position_binding)
		normals = program\has_attribute(@attributes.normal_binding)
		textures = program\has_attribute(@attributes.texture_coordinate_binding)

		return vertices and normals and textures

	save_model: (call) =>
		if is_compatible!
			positions_mesh = make_mesh(attributes.position_binding)
			normals_mesh = make_mesh(attributes.normal_binding)
			texture_coordinates_mesh = make_mesh(attributes.texture_coordinate_binding)

			model = make_model(positions_mesh, normals_mesh, texture_coordinates_mesh, call)
			model\save_dae(string.format("%s/%10d.dae", call.index))

	glDrawElements: (trace, call) =>
		Promise.keep("name", Promise.IsString(name))
		Promise.keep("index", Promise.IsNumber(index))

		save_model(call)

		return false

return GL3StaticModelHeuristic

-- This Source Code Form is subject to the terms of the Mozilla Public
-- License, v. 2.0. If a copy of the MPL was not distributed with this
-- file, You can obtain one at http://mozilla.org/MPL/2.0/.
--
-- Copyright 2016 [bk]door.maus

Promise = require "glurg.common.Promise"
BufferFilter = require "glurg.filters.BufferFilter"
ProgramFilter = require "glurg.filters.ProgramFilter"
VertexArrayFilter = require "glurg.filters.VertexArrayFilter"
MeshBlobBuilder = require "glurg.resources.MeshBlobBuilder"

class GL3StaticModelHeuristic
	new: (data) =>
		@array_buffer_filter = BufferFilter("GL_ARRAY_BUFFER")
		@element_array_buffer_filter = BufferFilter("GL_ELEMENT_ARRAY_BUFFER")
		@program_filter = ProgramFilter!
		@vertex_array_filter = VertexArrayFilter!
		@attributes =
			position_binding: data["position"]
			normal_binding: data["normal"]
			texture_coordinate_binding: data["texture"]
		@output_directory = data["output-dir"] or "."

	make_mesh: (attribute_name) =>
		program = @program_filter\get_current_program!
		attribute_index = program\get_attribute(attribute_name)

		vertex_array = @vertex_array_filter\get_current_vertex_array!
		description = vertex_array.descriptions[attribute_index]

		buffer = @array_buffer_filter\get_buffer(description.array_buffer)

		builder = MeshBlobBuilder!
		extracted_description = builder\extract_attrib_call(description.call)
		extracted_buffer = builder\extracted_buffer_call(buffer.data)

		if extract_description and extracted_buffer
			return Mesh(builder\build!)

	make_model: (positions_mesh, normals_mesh, texture_coordinates_mesh, call) =>
		model = Model(positions_mesh)
		model.normals = normals_mesh
		model.texture_coordinates[1] = texture_coordinates_mesh

		index_format = switch call\get_argument_by_name("type")\query!.value_name
			when 'GL_UNSIGNED_BYTE' then index_format = 'unsigned_byte'
			when 'GL_UNSIGNED_SHORT' then index_format = 'unsigned_short'
			when 'GL_UNSIGNED_INT' then index_format = 'unsigned_integer'
			else error "unknown index type"

		vertex_array = @vertex_array_filter\get_current_vertex_array!
		element_array_buffer = @element_array_buffer_filter\get_buffer(vertex_array.element_array_buffer)
		model\extract_index_data(index_format, element_array_buffer.data)

		return model

	is_compatible: =>
		program = @program_filter\get_current_program!

		vertices = program\has_attribute(@attributes.position_binding)
		normals = program\has_attribute(@attributes.normal_binding)
		textures = program\has_attribute(@attributes.texture_coordinate_binding)

		return vertices and normals and textures

	save_model: (call) =>
		if is_compatible!
			positions_mesh = make_mesh(attributes.position_binding)
			normals_mesh = make_mesh(attributes.normal_binding)
			texture_coordinates_mesh = make_mesh(attributes.texture_coordinate_binding)

			model = make_model(positions_mesh, normals_mesh, texture_coordinates_mesh, call)
			model\save_dae(string.format("%s/%10d.dae", call.index))

	glDrawElements: (trace, call) =>
		Promise.keep("name", Promise.IsString(name))
		Promise.keep("index", Promise.IsNumber(index))

		save_model(call)

		return false

return GL3StaticModelHeuristic