No gender for now on new model

No gender for now on new model

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

PPM2.BODYGROUP_SKELETON = 0
PPM2.BODYGROUP_GENDER = 1
PPM2.BODYGROUP_HORN = 2
PPM2.BODYGROUP_WINGS = 3
PPM2.BODYGROUP_MANE_UPPER = 4
PPM2.BODYGROUP_MANE_LOWER = 5
PPM2.BODYGROUP_TAIL = 6
PPM2.BODYGROUP_CMARK = 7
PPM2.BODYGROUP_EYELASH = 8

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

    @BODYGROUP_SKELETON = 0
    @BODYGROUP_GENDER = 1
    @BODYGROUP_HORN = 2
    @BODYGROUP_WINGS = 3
    @BODYGROUP_MANE_UPPER = 4
    @BODYGROUP_MANE_LOWER = 5
    @BODYGROUP_TAIL = 6
    @BODYGROUP_CMARK = 7
    @BODYGROUP_EYELASH = 8

    new: (controller) =>
        @ent = controller.ent
        @entID = controller.entID
        @controller = controller

    GetData: => @controller
    GetEntity: => @ent
    GetEntityID: => @entID
    GetDataID: => @entID

    ApplyRace: =>
        switch @controller\GetRace()
            when PPM2.RACE_EARTH
                @ent\SetBodygroup(@@BODYGROUP_HORN, 1)
                @ent\SetBodygroup(@@BODYGROUP_WINGS, 1)
            when PPM2.RACE_PEGASUS
                @ent\SetBodygroup(@@BODYGROUP_HORN, 1)
                @ent\SetBodygroup(@@BODYGROUP_WINGS, 0)
            when PPM2.RACE_UNICORN
                @ent\SetBodygroup(@@BODYGROUP_HORN, 0)
                @ent\SetBodygroup(@@BODYGROUP_WINGS, 1)
            when PPM2.RACE_ALICORN
                @ent\SetBodygroup(@@BODYGROUP_HORN, 0)
                @ent\SetBodygroup(@@BODYGROUP_WINGS, 0)

    ApplyBodygroups: =>
        @ent\SetBodygroup(@@BODYGROUP_MANE_UPPER, @controller\GetManeType())
        @ent\SetBodygroup(@@BODYGROUP_MANE_LOWER, @controller\GetManeTypeLower())
        @ent\SetBodygroup(@@BODYGROUP_TAIL, @controller\GetTailType())
        @ent\SetBodygroup(@@BODYGROUP_EYELASH, @controller\GetEyelashType())
        @ent\SetBodygroup(@@BODYGROUP_GENDER, @controller\GetGender())
        @ApplyRace()
    DataChanges: (state) =>
        switch state\GetKey()
            when 'ManeType'
                @ent\SetBodygroup(@@BODYGROUP_MANE_UPPER, @controller\GetManeType())
            when 'ManeTypeLower'
                @ent\SetBodygroup(@@BODYGROUP_MANE_LOWER, @controller\GetManeTypeLower())
            when 'TailType'
                @ent\SetBodygroup(@@BODYGROUP_TAIL, @controller\GetTailType())
            when 'EyelashType'
                @ent\SetBodygroup(@@BODYGROUP_EYELASH, @controller\GetEyelashType())
            when 'Gender'
                @ent\SetBodygroup(@@BODYGROUP_GENDER, @controller\GetGender())
            when 'Race'
                @ApplyRace()

class CPPMBodygroupController extends DefaultBodygroupController
    @MODELS = {'models/cppm/player_default_base.mdl', 'models/cppm/player_default_base_nj.mdl'}

    new: (...) => super(...)

    ApplyRace: =>
        switch @controller\GetRace()
            when PPM2.RACE_EARTH
                @ent\SetBodygroup(@@BODYGROUP_HORN, 1)
                @ent\SetBodygroup(@@BODYGROUP_WINGS, 1)
            when PPM2.RACE_PEGASUS
                @ent\SetBodygroup(@@BODYGROUP_HORN, 1)
                @ent\SetBodygroup(@@BODYGROUP_WINGS, 0)
            when PPM2.RACE_UNICORN
                @ent\SetBodygroup(@@BODYGROUP_HORN, 0)
                @ent\SetBodygroup(@@BODYGROUP_WINGS, 1)
            when PPM2.RACE_ALICORN
                @ent\SetBodygroup(@@BODYGROUP_HORN, 2)
                @ent\SetBodygroup(@@BODYGROUP_WINGS, 3)

class NewBodygroupController extends DefaultBodygroupController
    @MODELS = {'models/ppm/player_default_base_new.mdl'}

    @ATTACHMENT_EYES = 4
    @ATTACHMENT_EYES_NAME = 'eyes'

    @BODYGROUP_SKELETON = 0
    @BODYGROUP_GENDER = -1
    @BODYGROUP_HORN = 1
    @BODYGROUP_WINGS = 2

    CreateUpperManeModel: =>
        @maneModelUP\Remove() if IsValid(@maneModelUP)

        for ent in *ents.GetAll()
            if ent.isManeModel and ent.upperMane and ent.manePlayer == @ent
                ent\Remove()

        modelID, bodygroupID = PPM2.TransformNewModelID(@controller\GetManeTypeNew())
        modelID = "0" .. modelID if modelID < 10
        model = "models/ppm/hair/ppm_manesetupper#{modelID}.mdl"

        return if CLIENT and @controller\IsGoingToNetwork()
        @maneModelUP = ents.Create('prop_dynamic') if SERVER
        @maneModelUP = ClientsideModel(model) if CLIENT
        with @maneModelUP
            .isManeModel = true
            .upperMane = true
            .manePlayer = @ent
            \SetModel(model)
            \SetPos(@ent\GetPos())
            \Spawn()
            \Activate()
            \SetBodygroup(1, bodygroupID)
            \SetParent(@ent)
            \Fire('SetParentAttachment', @@ATTACHMENT_EYES_NAME) if SERVER
            \AddEffects(EF_BONEMERGE)

        if SERVER
            timer.Simple .5, ->
                return unless IsValid(@maneModelUP)
                @controller\SetUpperManeModel(@maneModelUP)
                @ent\SetNWEntity('PPM2.UpperManeModel', @maneModelUP) if IsValid(@ent)
        else
            @controller\SetUpperManeModel(@maneModelUP)

        return @maneModelUP
    CreateLowerManeModel: =>
        @maneModelLower\Remove() if IsValid(@maneModelLower)

        for ent in *ents.GetAll()
            if ent.isManeModel and ent.lowerMane and ent.manePlayer == @ent
                ent\Remove()

        modelID, bodygroupID = PPM2.TransformNewModelID(@controller\GetManeTypeLowerNew())
        modelID = "0" .. modelID if modelID < 10
        model = "models/ppm/hair/ppm_manesetlower#{modelID}.mdl"

        return if CLIENT and @controller\IsGoingToNetwork()
        @maneModelLower = ents.Create('prop_dynamic') if SERVER
        @maneModelLower = ClientsideModel(model) if CLIENT
        with @maneModelLower
            .isManeModel = true
            .lowerMane = true
            .manePlayer = @ent
            \SetModel(model)
            \SetPos(@ent\GetPos())
            \Spawn()
            \Activate()
            \SetBodygroup(1, bodygroupID)
            \SetParent(@ent)
            \Fire('SetParentAttachment', @@ATTACHMENT_EYES_NAME) if SERVER
            \AddEffects(EF_BONEMERGE)

        if SERVER
            timer.Simple .5, ->
                return unless IsValid(@maneModelLower)
                @controller\SetLowerManeModel(@maneModelLower)
                @ent\SetNWEntity('PPM2.LowerManeModel', @maneModelLower) if IsValid(@ent)
        else
            @controller\SetLowerManeModel(@maneModelLower)

        return @maneModelLower
    CreateTailModel: =>
        @tailModel\Remove() if IsValid(@tailModel)

        for ent in *ents.GetAll()
            if ent.isManeModel and ent.isTail and ent.manePlayer == @ent
                ent\Remove()

        modelID, bodygroupID = PPM2.TransformNewModelID(@controller\GetTailTypeNew())
        modelID = "0" .. modelID if modelID < 10
        model = "models/ppm/hair/ppm_tailset#{modelID}.mdl"

        return if CLIENT and @controller\IsGoingToNetwork()
        @tailModel = ents.Create('prop_dynamic') if SERVER
        @tailModel = ClientsideModel(model) if CLIENT
        with @tailModel
            .isManeModel = true
            .isTail = true
            .manePlayer = @ent
            \SetModel(model)
            \SetPos(@ent\GetPos())
            \Spawn()
            \Activate()
            \SetBodygroup(1, bodygroupID)
            \SetParent(@ent)
            \Fire('SetParentAttachment', @@ATTACHMENT_EYES_NAME) if SERVER
            \AddEffects(EF_BONEMERGE)

        if SERVER
            timer.Simple .5, ->
                return unless IsValid(@tailModel)
                @controller\SetTailModel(@tailModel)
                @ent\SetNWEntity('PPM2.TailModel', @tailModel) if IsValid(@ent)
        else
            @controller\SetTailModel(@tailModel)

        return @tailModel

    CreateUpperManeModelIfNotExists: =>
        return if CLIENT and @controller\IsGoingToNetwork()
        @CreateUpperManeModel() if not IsValid(@maneModelUP)
        return @maneModelUP
    CreateLowerManeModelIfNotExists: =>
        return if CLIENT and @controller\IsGoingToNetwork()
        @CreateLowerManeModel() if not IsValid(@maneModelLower)
        return @maneModelLower
    CreateTailModelIfNotExists: =>
        return if CLIENT and @controller\IsGoingToNetwork()
        @CreateTailModel() if not IsValid(@tailModel)
        return @tailModel

    GetUpperMane: => @maneModelUP or NULL
    GetLowerMane: => @maneModelLower or NULL
    GetTail: => @tailModel or NULL

    MergeModels: (targetEnt = NULL) =>
        return unless IsValid(targetEnt)
        maneUpper = @CreateUpperManeModelIfNotExists()
        maneLower = @CreateLowerManeModelIfNotExists()
        tail = @CreateTailModelIfNotExists()
        if IsValid(maneUpper)
            maneUpper\SetParent(targetEnt)
            maneUpper\Fire('SetParentAttachment', @@ATTACHMENT_EYES_NAME) if SERVER
        if IsValid(maneLower)
            maneLower\SetParent(targetEnt)
            maneLower\Fire('SetParentAttachment', @@ATTACHMENT_EYES_NAME) if SERVER
        if IsValid(tail)
            tail\SetParent(targetEnt)
            tail\Fire('SetParentAttachment', @@ATTACHMENT_EYES_NAME) if SERVER

    UpdateUpperMane: =>
        return if CLIENT and @controller\IsGoingToNetwork()
        @CreateUpperManeModelIfNotExists()
        modelID, bodygroupID = PPM2.TransformNewModelID(@controller\GetManeTypeNew())
        modelID = "0" .. modelID if modelID < 10
        model = "models/ppm/hair/ppm_manesetupper#{modelID}.mdl"
        @maneModelUP\SetModel(model)
        @maneModelUP\SetBodygroup(1, bodygroupID)
        return @maneModelUP
    UpdateLowerMane: =>
        return if CLIENT and @controller\IsGoingToNetwork()
        @CreateLowerManeModelIfNotExists()
        modelID, bodygroupID = PPM2.TransformNewModelID(@controller\GetManeTypeLowerNew())
        modelID = "0" .. modelID if modelID < 10
        model = "models/ppm/hair/ppm_manesetlower#{modelID}.mdl"
        @maneModelLower\SetModel(model)
        @maneModelLower\SetBodygroup(1, bodygroupID)
        return @maneModelLower
    UpdateTailModel: =>
        return if CLIENT and @controller\IsGoingToNetwork()
        @CreateTailModelIfNotExists()
        modelID, bodygroupID = PPM2.TransformNewModelID(@controller\GetTailTypeNew())
        modelID = "0" .. modelID if modelID < 10
        model = "models/ppm/hair/ppm_tailset#{modelID}.mdl"
        @tailModel\SetModel(model)
        @tailModel\SetBodygroup(1, bodygroupID)
        return @tailModel

    ApplyBodygroups: =>
        @ent\SetBodygroup(@@BODYGROUP_EYELASH, @controller\GetEyelashType())
        @ent\SetBodygroup(@@BODYGROUP_GENDER, @controller\GetGender())
        @ApplyRace()
        @CreateUpperManeModel()
        @CreateLowerManeModel()
        @CreateTailModel()

    DataChanges: (state) =>
        switch state\GetKey()
            when 'EyelashType'
                @ent\SetBodygroup(@@BODYGROUP_EYELASH, @controller\GetEyelashType())
            when 'Gender'
                @ent\SetBodygroup(@@BODYGROUP_GENDER, @controller\GetGender())
            when 'ManeTypeNew'
                @UpdateUpperMane()
            when 'ManeTypeLowerNew'
                @UpdateLowerMane()
            when 'TailTypeNew'
                @UpdateTailModel()
            when 'Race'
                @ApplyRace()

PPM2.CPPMBodygroupController = CPPMBodygroupController
PPM2.DefaultBodygroupController = DefaultBodygroupController

PPM2.GetBodugroupController = (model = 'models/ppm/player_default_base.mdl') -> DefaultBodygroupController.AVALIABLE_CONTROLLERS[model\lower()] or DefaultBodygroupController

--
-- 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.BODYGROUP_SKELETON = 0
PPM2.BODYGROUP_GENDER = 1
PPM2.BODYGROUP_HORN = 2
PPM2.BODYGROUP_WINGS = 3
PPM2.BODYGROUP_MANE_UPPER = 4
PPM2.BODYGROUP_MANE_LOWER = 5
PPM2.BODYGROUP_TAIL = 6
PPM2.BODYGROUP_CMARK = 7
PPM2.BODYGROUP_EYELASH = 8

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

    @BODYGROUP_SKELETON = 0
    @BODYGROUP_GENDER = 1
    @BODYGROUP_HORN = 2
    @BODYGROUP_WINGS = 3
    @BODYGROUP_MANE_UPPER = 4
    @BODYGROUP_MANE_LOWER = 5
    @BODYGROUP_TAIL = 6
    @BODYGROUP_CMARK = 7
    @BODYGROUP_EYELASH = 8

    new: (controller) =>
        @ent = controller.ent
        @entID = controller.entID
        @controller = controller

    GetData: => @controller
    GetEntity: => @ent
    GetEntityID: => @entID
    GetDataID: => @entID

    ApplyRace: =>
        switch @controller\GetRace()
            when PPM2.RACE_EARTH
                @ent\SetBodygroup(@@BODYGROUP_HORN, 1)
                @ent\SetBodygroup(@@BODYGROUP_WINGS, 1)
            when PPM2.RACE_PEGASUS
                @ent\SetBodygroup(@@BODYGROUP_HORN, 1)
                @ent\SetBodygroup(@@BODYGROUP_WINGS, 0)
            when PPM2.RACE_UNICORN
                @ent\SetBodygroup(@@BODYGROUP_HORN, 0)
                @ent\SetBodygroup(@@BODYGROUP_WINGS, 1)
            when PPM2.RACE_ALICORN
                @ent\SetBodygroup(@@BODYGROUP_HORN, 0)
                @ent\SetBodygroup(@@BODYGROUP_WINGS, 0)

    ApplyBodygroups: =>
        @ent\SetBodygroup(@@BODYGROUP_MANE_UPPER, @controller\GetManeType())
        @ent\SetBodygroup(@@BODYGROUP_MANE_LOWER, @controller\GetManeTypeLower())
        @ent\SetBodygroup(@@BODYGROUP_TAIL, @controller\GetTailType())
        @ent\SetBodygroup(@@BODYGROUP_EYELASH, @controller\GetEyelashType())
        @ent\SetBodygroup(@@BODYGROUP_GENDER, @controller\GetGender())
        @ApplyRace()
    DataChanges: (state) =>
        switch state\GetKey()
            when 'ManeType'
                @ent\SetBodygroup(@@BODYGROUP_MANE_UPPER, @controller\GetManeType())
            when 'ManeTypeLower'
                @ent\SetBodygroup(@@BODYGROUP_MANE_LOWER, @controller\GetManeTypeLower())
            when 'TailType'
                @ent\SetBodygroup(@@BODYGROUP_TAIL, @controller\GetTailType())
            when 'EyelashType'
                @ent\SetBodygroup(@@BODYGROUP_EYELASH, @controller\GetEyelashType())
            when 'Gender'
                @ent\SetBodygroup(@@BODYGROUP_GENDER, @controller\GetGender())
            when 'Race'
                @ApplyRace()

class CPPMBodygroupController extends DefaultBodygroupController
    @MODELS = {'models/cppm/player_default_base.mdl', 'models/cppm/player_default_base_nj.mdl'}

    new: (...) => super(...)

    ApplyRace: =>
        switch @controller\GetRace()
            when PPM2.RACE_EARTH
                @ent\SetBodygroup(@@BODYGROUP_HORN, 1)
                @ent\SetBodygroup(@@BODYGROUP_WINGS, 1)
            when PPM2.RACE_PEGASUS
                @ent\SetBodygroup(@@BODYGROUP_HORN, 1)
                @ent\SetBodygroup(@@BODYGROUP_WINGS, 0)
            when PPM2.RACE_UNICORN
                @ent\SetBodygroup(@@BODYGROUP_HORN, 0)
                @ent\SetBodygroup(@@BODYGROUP_WINGS, 1)
            when PPM2.RACE_ALICORN
                @ent\SetBodygroup(@@BODYGROUP_HORN, 2)
                @ent\SetBodygroup(@@BODYGROUP_WINGS, 3)

class NewBodygroupController extends DefaultBodygroupController
    @MODELS = {'models/ppm/player_default_base_new.mdl'}

    @ATTACHMENT_EYES = 4
    @ATTACHMENT_EYES_NAME = 'eyes'

    CreateUpperManeModel: =>
        @maneModelUP\Remove() if IsValid(@maneModelUP)

        for ent in *ents.GetAll()
            if ent.isManeModel and ent.upperMane and ent.manePlayer == @ent
                ent\Remove()

        modelID, bodygroupID = PPM2.TransformNewModelID(@controller\GetManeTypeNew())
        modelID = "0" .. modelID if modelID < 10
        model = "models/ppm/hair/ppm_manesetupper#{modelID}.mdl"

        return if CLIENT and @controller\IsGoingToNetwork()
        @maneModelUP = ents.Create('prop_dynamic') if SERVER
        @maneModelUP = ClientsideModel(model) if CLIENT
        with @maneModelUP
            .isManeModel = true
            .upperMane = true
            .manePlayer = @ent
            \SetModel(model)
            \SetPos(@ent\GetPos())
            \Spawn()
            \Activate()
            \SetBodygroup(1, bodygroupID)
            \SetParent(@ent)
            \Fire('SetParentAttachment', @@ATTACHMENT_EYES_NAME) if SERVER
            \AddEffects(EF_BONEMERGE)

        if SERVER
            timer.Simple .5, ->
                return unless IsValid(@maneModelUP)
                @controller\SetUpperManeModel(@maneModelUP)
                @ent\SetNWEntity('PPM2.UpperManeModel', @maneModelUP) if IsValid(@ent)
        else
            @controller\SetUpperManeModel(@maneModelUP)

        return @maneModelUP
    CreateLowerManeModel: =>
        @maneModelLower\Remove() if IsValid(@maneModelLower)

        for ent in *ents.GetAll()
            if ent.isManeModel and ent.lowerMane and ent.manePlayer == @ent
                ent\Remove()

        modelID, bodygroupID = PPM2.TransformNewModelID(@controller\GetManeTypeLowerNew())
        modelID = "0" .. modelID if modelID < 10
        model = "models/ppm/hair/ppm_manesetlower#{modelID}.mdl"

        return if CLIENT and @controller\IsGoingToNetwork()
        @maneModelLower = ents.Create('prop_dynamic') if SERVER
        @maneModelLower = ClientsideModel(model) if CLIENT
        with @maneModelLower
            .isManeModel = true
            .lowerMane = true
            .manePlayer = @ent
            \SetModel(model)
            \SetPos(@ent\GetPos())
            \Spawn()
            \Activate()
            \SetBodygroup(1, bodygroupID)
            \SetParent(@ent)
            \Fire('SetParentAttachment', @@ATTACHMENT_EYES_NAME) if SERVER
            \AddEffects(EF_BONEMERGE)

        if SERVER
            timer.Simple .5, ->
                return unless IsValid(@maneModelLower)
                @controller\SetLowerManeModel(@maneModelLower)
                @ent\SetNWEntity('PPM2.LowerManeModel', @maneModelLower) if IsValid(@ent)
        else
            @controller\SetLowerManeModel(@maneModelLower)

        return @maneModelLower
    CreateTailModel: =>
        @tailModel\Remove() if IsValid(@tailModel)

        for ent in *ents.GetAll()
            if ent.isManeModel and ent.isTail and ent.manePlayer == @ent
                ent\Remove()

        modelID, bodygroupID = PPM2.TransformNewModelID(@controller\GetTailTypeNew())
        modelID = "0" .. modelID if modelID < 10
        model = "models/ppm/hair/ppm_tailset#{modelID}.mdl"

        return if CLIENT and @controller\IsGoingToNetwork()
        @tailModel = ents.Create('prop_dynamic') if SERVER
        @tailModel = ClientsideModel(model) if CLIENT
        with @tailModel
            .isManeModel = true
            .isTail = true
            .manePlayer = @ent
            \SetModel(model)
            \SetPos(@ent\GetPos())
            \Spawn()
            \Activate()
            \SetBodygroup(1, bodygroupID)
            \SetParent(@ent)
            \Fire('SetParentAttachment', @@ATTACHMENT_EYES_NAME) if SERVER
            \AddEffects(EF_BONEMERGE)

        if SERVER
            timer.Simple .5, ->
                return unless IsValid(@tailModel)
                @controller\SetTailModel(@tailModel)
                @ent\SetNWEntity('PPM2.TailModel', @tailModel) if IsValid(@ent)
        else
            @controller\SetTailModel(@tailModel)

        return @tailModel

    CreateUpperManeModelIfNotExists: =>
        return if CLIENT and @controller\IsGoingToNetwork()
        @CreateUpperManeModel() if not IsValid(@maneModelUP)
        return @maneModelUP
    CreateLowerManeModelIfNotExists: =>
        return if CLIENT and @controller\IsGoingToNetwork()
        @CreateLowerManeModel() if not IsValid(@maneModelLower)
        return @maneModelLower
    CreateTailModelIfNotExists: =>
        return if CLIENT and @controller\IsGoingToNetwork()
        @CreateTailModel() if not IsValid(@tailModel)
        return @tailModel

    GetUpperMane: => @maneModelUP or NULL
    GetLowerMane: => @maneModelLower or NULL
    GetTail: => @tailModel or NULL

    MergeModels: (targetEnt = NULL) =>
        return unless IsValid(targetEnt)
        maneUpper = @CreateUpperManeModelIfNotExists()
        maneLower = @CreateLowerManeModelIfNotExists()
        tail = @CreateTailModelIfNotExists()
        if IsValid(maneUpper)
            maneUpper\SetParent(targetEnt)
            maneUpper\Fire('SetParentAttachment', @@ATTACHMENT_EYES_NAME) if SERVER
        if IsValid(maneLower)
            maneLower\SetParent(targetEnt)
            maneLower\Fire('SetParentAttachment', @@ATTACHMENT_EYES_NAME) if SERVER
        if IsValid(tail)
            tail\SetParent(targetEnt)
            tail\Fire('SetParentAttachment', @@ATTACHMENT_EYES_NAME) if SERVER

    UpdateUpperMane: =>
        return if CLIENT and @controller\IsGoingToNetwork()
        @CreateUpperManeModelIfNotExists()
        modelID, bodygroupID = PPM2.TransformNewModelID(@controller\GetManeTypeNew())
        modelID = "0" .. modelID if modelID < 10
        model = "models/ppm/hair/ppm_manesetupper#{modelID}.mdl"
        @maneModelUP\SetModel(model)
        @maneModelUP\SetBodygroup(1, bodygroupID)
        return @maneModelUP
    UpdateLowerMane: =>
        return if CLIENT and @controller\IsGoingToNetwork()
        @CreateLowerManeModelIfNotExists()
        modelID, bodygroupID = PPM2.TransformNewModelID(@controller\GetManeTypeLowerNew())
        modelID = "0" .. modelID if modelID < 10
        model = "models/ppm/hair/ppm_manesetlower#{modelID}.mdl"
        @maneModelLower\SetModel(model)
        @maneModelLower\SetBodygroup(1, bodygroupID)
        return @maneModelLower
    UpdateTailModel: =>
        return if CLIENT and @controller\IsGoingToNetwork()
        @CreateTailModelIfNotExists()
        modelID, bodygroupID = PPM2.TransformNewModelID(@controller\GetTailTypeNew())
        modelID = "0" .. modelID if modelID < 10
        model = "models/ppm/hair/ppm_tailset#{modelID}.mdl"
        @tailModel\SetModel(model)
        @tailModel\SetBodygroup(1, bodygroupID)
        return @tailModel

    ApplyBodygroups: =>
        @ent\SetBodygroup(@@BODYGROUP_EYELASH, @controller\GetEyelashType())
        @ent\SetBodygroup(@@BODYGROUP_GENDER, @controller\GetGender())
        @ApplyRace()
        @CreateUpperManeModel()
        @CreateLowerManeModel()
        @CreateTailModel()

    DataChanges: (state) =>
        switch state\GetKey()
            when 'EyelashType'
                @ent\SetBodygroup(@@BODYGROUP_EYELASH, @controller\GetEyelashType())
            when 'Gender'
                @ent\SetBodygroup(@@BODYGROUP_GENDER, @controller\GetGender())
            when 'ManeTypeNew'
                @UpdateUpperMane()
            when 'ManeTypeLowerNew'
                @UpdateLowerMane()
            when 'TailTypeNew'
                @UpdateTailModel()
            when 'Race'
                @ApplyRace()

PPM2.CPPMBodygroupController = CPPMBodygroupController
PPM2.DefaultBodygroupController = DefaultBodygroupController

PPM2.GetBodugroupController = (model = 'models/ppm/player_default_base.mdl') -> DefaultBodygroupController.AVALIABLE_CONTROLLERS[model\lower()] or DefaultBodygroupController


Fixed backups

Fixed backups

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

class PonyDataInstance
    @DATA_DIR = "ppm2/"
    @DATA_DIR_BACKUP = "ppm2/backups/"
    @PONY_DATA = {
        'age': {
            default: -> PPM2.AGE_ADULT
            getFunc: 'Age'
            enum: {'FILLY', 'ADULT', 'MATURE'}
            fix: (arg = PPM2.AGE_ADULT) -> math.Clamp(tonumber(arg) or PPM2.AGE_ADULT, 0, 2)
        }

        'race': {
            default: -> PPM2.RACE_EARTH
            getFunc: 'Race'
            enum: [arg for arg in *PPM2.RACE_ENUMS]
            fix: (arg = PPM2.RACE_EARTH) -> math.Clamp(tonumber(arg) or PPM2.RACE_EARTH, 0, 3)
        }

        'gender': {
            default: -> PPM2.GENDER_FEMALE
            getFunc: 'Gender'
            enum: [arg for arg in *PPM2.AGE_ENUMS]
            fix: (arg = PPM2.GENDER_FEMALE) -> math.Clamp(tonumber(arg) or PPM2.GENDER_FEMALE, 0, 1)
        }

        'weight': {
            default: -> 1
            getFunc: 'Weight'
            fix: (arg = 1) -> math.Clamp(tonumber(arg) or 1, PPM2.MIN_WEIGHT, PPM2.MAX_WEIGHT)
        }

        'eyelash': {
            default: -> 0
            getFunc: 'EyelashType'
            enum: [arg for arg in *PPM2.EyelashTypes]
            fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_EYELASHES, PPM2.MAX_EYELASHES)
        }

        'tail': {
            default: -> 0
            getFunc: 'TailType'
            enum: [arg for arg in *PPM2.AvaliableTails] -- fast copy
            fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_TAILS, PPM2.MAX_TAILS)
        }

        'mane': {
            default: -> 0
            getFunc: 'ManeType'
            enum: [arg for arg in *PPM2.AvaliableUpperManes] -- fast copy
            fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_UPPER_MANES, PPM2.MAX_UPPER_MANES)
        }

        'manelower': {
            default: -> 0
            getFunc: 'ManeTypeLower'
            enum: [arg for arg in *PPM2.AvaliableLowerManes] -- fast copy
            fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_LOWER_MANES, PPM2.MAX_LOWER_MANES)
        }

        'tailsize': {
            default: -> 1
            getFunc: 'TailSize'
            fix: (arg = 1) -> math.Clamp(tonumber(arg) or 1, PPM2.MIN_TAIL_SIZE, PPM2.MAX_TAIL_SIZE)
        }

        'eye_iris_size': {
            default: -> 1
            getFunc: 'IrisSize'
            fix: (arg = 1) -> math.Clamp(tonumber(arg) or 1, PPM2.MIN_IRIS, PPM2.MAX_IRIS)
        }

        'hole_width': {
            default: -> 1
            getFunc: 'HoleWidth'
            fix: (arg = 1) -> math.Clamp(tonumber(arg) or 1, PPM2.MIN_PUPIL_SIZE, PPM2.MAX_PUPIL_SIZE)
        }

        'eye_hole_size': {
            default: -> .8
            getFunc: 'HoleSize'
            fix: (arg = 1) -> math.Clamp(tonumber(arg) or .8, PPM2.MIN_HOLE, PPM2.MAX_HOLE)
        }

        'cmark': {
            default: -> true
            getFunc: 'CMark'
            fix: (arg = true) -> tobool(arg)
        }

        'cmark_type': {
            default: -> 4
            getFunc: 'CMarkType'
            enum: [arg for arg in *PPM2.DefaultCutiemarks]
            fix: (arg = 4) -> math.Clamp(tonumber(arg) or 4, PPM2.MIN_CMARK, PPM2.MAX_CMARK)
        }

        'cmark_url': {
            default: -> ''
            getFunc: 'CMarkURL'
            fix: (arg = '') ->
                arg = tostring(arg)
                if arg\find('^https?://')
                    return arg
                else
                    return ''
        }

        'eye_bg': {
            default: -> Color(255, 255, 255)
            getFunc: 'EyeBackground'
            fix: (arg = Color(255, 255, 255)) ->
                if type(arg) ~= 'table'
                    return Color(255, 255, 255)
                else
                    {:r, :g, :b, :a} = arg
                    if r and g and b and a
                        return Color(r, g, b, a)
                    else
                        return Color(255, 255, 255)
        }

        'eye_hole': {
            default: -> Color(0, 0, 0)
            getFunc: 'EyeHole'
            fix: (arg = Color(0, 0, 0)) ->
                if type(arg) ~= 'table'
                    return Color(0, 0, 0)
                else
                    {:r, :g, :b, :a} = arg
                    if r and g and b and a
                        return Color(r, g, b, a)
                    else
                        return Color(0, 0, 0)
        }

        'eye_iris1': {
            default: -> Color(200, 200, 200)
            getFunc: 'EyeIrisTop'
            fix: (arg = Color(200, 200, 200)) ->
                if type(arg) ~= 'table'
                    return Color(200, 200, 200)
                else
                    {:r, :g, :b, :a} = arg
                    if r and g and b and a
                        return Color(r, g, b, a)
                    else
                        return Color(200, 200, 200)
        }

        'eye_iris2': {
            default: -> Color(200, 200, 200)
            getFunc: 'EyeIrisBottom'
            fix: (arg = Color(200, 200, 200)) ->
                if type(arg) ~= 'table'
                    return Color(200, 200, 200)
                else
                    {:r, :g, :b, :a} = arg
                    if r and g and b and a
                        return Color(r, g, b, a)
                    else
                        return Color(200, 200, 200)
        }

        'eye_irisline1': {
            default: -> Color(255, 255, 255)
            getFunc: 'EyeIrisLine1'
            fix: (arg = Color(255, 255, 255)) ->
                if type(arg) ~= 'table'
                    return Color(255, 255, 255)
                else
                    {:r, :g, :b, :a} = arg
                    if r and g and b and a
                        return Color(r, g, b, a)
                    else
                        return Color(255, 255, 255)
        }

        'eye_irisline2': {
            default: -> Color(255, 255, 255)
            getFunc: 'EyeIrisLine2'
            fix: (arg = Color(255, 255, 255)) ->
                if type(arg) ~= 'table'
                    return Color(255, 255, 255)
                else
                    {:r, :g, :b, :a} = arg
                    if r and g and b and a
                        return Color(r, g, b, a)
                    else
                        return Color(255, 255, 255)
        }

        'body': {
            default: -> Color(255, 255, 255)
            getFunc: 'BodyColor'
            fix: (arg = Color(255, 255, 255)) ->
                if type(arg) ~= 'table'
                    return Color(255, 255, 255)
                else
                    {:r, :g, :b, :a} = arg
                    if r and g and b and a
                        return Color(r, g, b, a)
                    else
                        return Color(255, 255, 255)
        }

        'eye_lines': {
            default: -> true
            getFunc: 'EyeLines'
            fix: (arg = true) -> tobool(arg)
        }
    }

    for i = 1, 6
        @PONY_DATA["mane_color_#{i}"] = {
            default: -> Color(255, 255, 255)
            getFunc: "ManeColor#{i}"
            fix: (arg = Color(255, 255, 255)) ->
                if type(arg) ~= 'table'
                    return Color(255, 255, 255)
                else
                    {:r, :g, :b, :a} = arg
                    if r and g and b and a
                        return Color(r, g, b, a)
                    else
                        return Color(255, 255, 255)
        }

        @PONY_DATA["mane_detail_color_#{i}"] = {
            default: -> Color(255, 255, 255)
            getFunc: "ManeDetailColor#{i}"
            fix: (arg = Color(255, 255, 255)) ->
                if type(arg) ~= 'table'
                    return Color(255, 255, 255)
                else
                    {:r, :g, :b, :a} = arg
                    if r and g and b and a
                        return Color(r, g, b, a)
                    else
                        return Color(255, 255, 255)
        }

        @PONY_DATA["tail_detail_color_#{i}"] = {
            default: -> Color(255, 255, 255)
            getFunc: "TailDetailColor#{i}"
            fix: (arg = Color(255, 255, 255)) ->
                if type(arg) ~= 'table'
                    return Color(255, 255, 255)
                else
                    {:r, :g, :b, :a} = arg
                    if r and g and b and a
                        return Color(r, g, b, a)
                    else
                        return Color(255, 255, 255)
        }

        @PONY_DATA["tail_color_#{i}"] = {
            default: -> Color(255, 255, 255)
            getFunc: "TailColor#{i}"
            fix: (arg = Color(255, 255, 255)) ->
                if type(arg) ~= 'table'
                    return Color(255, 255, 255)
                else
                    {:r, :g, :b, :a} = arg
                    if r and g and b and a
                        return Color(r, g, b, a)
                    else
                        return Color(255, 255, 255)
        }

        -- Reserved - they can be accessed/used/changed, but they do not do anything
        @PONY_DATA["lower_mane_color_#{i}"] = {
            default: -> Color(255, 255, 255)
            getFunc: "LowerManeColor#{i}"
            fix: (arg = Color(255, 255, 255)) ->
                if type(arg) ~= 'table'
                    return Color(255, 255, 255)
                else
                    {:r, :g, :b, :a} = arg
                    if r and g and b and a
                        return Color(r, g, b, a)
                    else
                        return Color(255, 255, 255)
        }

        @PONY_DATA["upper_mane_color_#{i}"] = {
            default: -> Color(255, 255, 255)
            getFunc: "UpperManeColor#{i}"
            fix: (arg = Color(255, 255, 255)) ->
                if type(arg) ~= 'table'
                    return Color(255, 255, 255)
                else
                    {:r, :g, :b, :a} = arg
                    if r and g and b and a
                        return Color(r, g, b, a)
                    else
                        return Color(255, 255, 255)
        }
        ------

    @PONY_DATA_MAPPING = {getFunc\lower(), key for key, {:getFunc} in pairs @PONY_DATA}
    @PONY_DATA_MAPPING[key] = key for key, value in pairs @PONY_DATA

    for key, data in pairs @PONY_DATA
        continue unless data.enum
        data.enum = [arg\upper() for arg in *data.enum]
        data.enumMapping = {}
        data.enumMappingBackward = {}
        i = -1
        for enumVal in *data.enum
            i += 1
            data.enumMapping[i] = enumVal
            data.enumMappingBackward[enumVal] = i
    for key, {:getFunc, :fix, :enumMappingBackward, :enumMapping} in pairs @PONY_DATA
        @__base["Get#{getFunc}"] = => @dataTable[key]
        if enumMapping
            @__base["Get#{getFunc}Enum"] = => enumMapping[@dataTable[key]] or enumMapping[0] or @dataTable[key]
            @__base["GetEnum#{getFunc}"] = @__base["Get#{getFunc}Enum"]
		@__base["Set#{getFunc}"] = (val = defValue, ...) =>
            if type(val) == 'string' and enumMappingBackward
                newVal = enumMappingBackward[val\upper()]
                val = newVal if newVal
            newVal = fix(val)
			oldVal = @dataTable[key]
			@dataTable[key] = newVal
            @ValueChanges(key, oldVal, newVal, ...)

    new: (filename, data, readIfExists = true, force = false, doBackup = true) =>
        @SetFilename(filename)
        @valid = @isOpen
        @rawData = data
        @dataTable = {k, default() for k, {:default} in pairs @@PONY_DATA}
        if data
            @SetupData(data, true)
        elseif @exists and readIfExists
            @ReadFromDisk(force, doBackup)

    @ERR_MISSING_PARAMETER = 4
    @ERR_MISSING_CONTENT = 5
    SetupData: (data, force = false, doBackup = false) =>
        if doBackup or not force
            makeBackup = false
            for key, value in pairs data
                key = key\lower()
                map = @@PONY_DATA_MAPPING[key]
                if not map
                    return @@ERR_MISSING_PARAMETER if not force
                    makeBackup = true
                    break
                mapData = @@PONY_DATA[map]
                if mapData.enum
                    if type(value) == 'string' and not mapData.enumMappingBackward[value\upper()] or type(value) == 'number' and not mapData.enumMapping[value]
                        return @@ERR_MISSING_CONTENT if not force
                        makeBackup = true
                        break
            if doBackup and makeBackup and @exists
                bkName = "#{@@DATA_DIR_BACKUP}#{@filename}_bak_#{os.date('%S_%M_%H-%d_%m_%Y', os.time())}.txt"
                fRead = file.Read(@fpath, 'DATA')
                file.Write(bkName, fRead)

        for key, value in pairs data
            key = key\lower()
            map = @@PONY_DATA_MAPPING[key]
            continue unless map
            mapData = @@PONY_DATA[map]
            if mapData.enum and type(value) == 'string'
                @dataTable[key] = mapData.fix(mapData.enumMappingBackward[value\upper()])
            else
                @dataTable[key] = mapData.fix(value)
    ValueChanges: (key, oldVal, newVal, saveNow = @exists) =>
        if @nwObj
            {:getFunc} = @@PONY_DATA[key]
            @nwObj["Set#{getFunc}"](@nwObj, newVal)
        @Save() if saveNow
    SetFilename: (filename) =>
        @filename = filename
        @filenameFull = "#{filename}.txt"
        @fpath = "#{@@DATA_DIR}#{filename}.txt"
        @fpathBackup = "#{@@DATA_DIR}#{filename}.bak.txt"
        @fpathFull = "data/#{@@DATA_DIR}#{filename}.txt"
        @isOpen = @filename ~= nil
        @exists = file.Exists(@fpath, 'DATA')
        return @exists
    SetNetworkData: (nwObj) => @nwObj = nwObj
    IsValid: => @valid
    Exists: => @exists
    FileExists: => @exists
    IsExists: => @exists
    GetFileName: => @filename
    GetFileNameFull: => @filenameFull
    GetFilePath: => @fpath
    GetFullFilePath: => @fpathFull
    SerealizeValue: (valID = '') =>
        map = @@PONY_DATA[valID]
        return unless map
        val = @dataTable[valID]
        if map.enum
            return map.enumMapping[val] or map.enumMapping[map.default()]
        elseif map.serealize
            return map.serealize(val)
        else
            return val
    Serealize: (prettyPrint = true) =>
        serTab = {key, @SerealizeValue(key) for key, val in pairs @dataTable}
        util.TableToJSON(serTab, prettyPrint)
    GetAsNetworked: => {getFunc, @dataTable[k] for k, {:getFunc} in pairs @@PONY_DATA}

    @READ_SUCCESS = 0
    @ERR_FILE_NOT_EXISTS = 1
    @ERR_FILE_EMPTY = 2
    @ERR_FILE_CORRUPT = 3
    ReadFromDisk: (force = false, doBackup = true) =>
        return @@ERR_FILE_NOT_EXISTS unless @exists
        fRead = file.Read(@fpath, 'DATA')
        return @@ERR_FILE_EMPTY if not fRead or fRead == ''
        readTable = util.JSONToTable(fRead)
        return @@ERR_FILE_CORRUPT unless readTable
        return @SetupData(readTable, force, doBackup) or @@READ_SUCCESS
    Save: (doBackup = true) =>
        if doBackup and @exists
            fRead = file.Read(@fpath, 'DATA')
            file.Write(@fpathBackup, fRead)
        fCreate = @Serealize()
        file.Write(@fpath, fCreate)
        @exists = true

PPM2.PonyDataInstance = PonyDataInstance

PPM2.MainDataInstance = nil
PPM2.GetMainData = ->
    if not PPM2.MainDataInstance
        PPM2.MainDataInstance = PonyDataInstance('_current', nil, true, true)
        if not PPM2.MainDataInstance\FileExists()
            PPM2.MainDataInstance\Save()
    return PPM2.MainDataInstance

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

class PonyDataInstance
    @DATA_DIR = "ppm2/"
    @DATA_DIR_BACKUP = "ppm2/backup/"
    @PONY_DATA = {
        'age': {
            default: -> PPM2.AGE_ADULT
            getFunc: 'Age'
            enum: {'FILLY', 'ADULT', 'MATURE'}
            fix: (arg = PPM2.AGE_ADULT) -> math.Clamp(tonumber(arg) or PPM2.AGE_ADULT, 0, 2)
        }

        'race': {
            default: -> PPM2.RACE_EARTH
            getFunc: 'Race'
            enum: [arg for arg in *PPM2.RACE_ENUMS]
            fix: (arg = PPM2.RACE_EARTH) -> math.Clamp(tonumber(arg) or PPM2.RACE_EARTH, 0, 3)
        }

        'gender': {
            default: -> PPM2.GENDER_FEMALE
            getFunc: 'Gender'
            enum: [arg for arg in *PPM2.AGE_ENUMS]
            fix: (arg = PPM2.GENDER_FEMALE) -> math.Clamp(tonumber(arg) or PPM2.GENDER_FEMALE, 0, 1)
        }

        'weight': {
            default: -> 1
            getFunc: 'Weight'
            fix: (arg = 1) -> math.Clamp(tonumber(arg) or 1, PPM2.MIN_WEIGHT, PPM2.MAX_WEIGHT)
        }

        'eyelash': {
            default: -> 0
            getFunc: 'EyelashType'
            enum: [arg for arg in *PPM2.EyelashTypes]
            fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_EYELASHES, PPM2.MAX_EYELASHES)
        }

        'tail': {
            default: -> 0
            getFunc: 'TailType'
            enum: [arg for arg in *PPM2.AvaliableTails] -- fast copy
            fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_TAILS, PPM2.MAX_TAILS)
        }

        'mane': {
            default: -> 0
            getFunc: 'ManeType'
            enum: [arg for arg in *PPM2.AvaliableUpperManes] -- fast copy
            fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_UPPER_MANES, PPM2.MAX_UPPER_MANES)
        }

        'manelower': {
            default: -> 0
            getFunc: 'ManeTypeLower'
            enum: [arg for arg in *PPM2.AvaliableLowerManes] -- fast copy
            fix: (arg = 0) -> math.Clamp(tonumber(arg) or 0, PPM2.MIN_LOWER_MANES, PPM2.MAX_LOWER_MANES)
        }

        'tailsize': {
            default: -> 1
            getFunc: 'TailSize'
            fix: (arg = 1) -> math.Clamp(tonumber(arg) or 1, PPM2.MIN_TAIL_SIZE, PPM2.MAX_TAIL_SIZE)
        }

        'eye_iris_size': {
            default: -> 1
            getFunc: 'IrisSize'
            fix: (arg = 1) -> math.Clamp(tonumber(arg) or 1, PPM2.MIN_IRIS, PPM2.MAX_IRIS)
        }

        'hole_width': {
            default: -> 1
            getFunc: 'HoleWidth'
            fix: (arg = 1) -> math.Clamp(tonumber(arg) or 1, PPM2.MIN_PUPIL_SIZE, PPM2.MAX_PUPIL_SIZE)
        }

        'eye_hole_size': {
            default: -> .8
            getFunc: 'HoleSize'
            fix: (arg = 1) -> math.Clamp(tonumber(arg) or .8, PPM2.MIN_HOLE, PPM2.MAX_HOLE)
        }

        'cmark': {
            default: -> true
            getFunc: 'CMark'
            fix: (arg = true) -> tobool(arg)
        }

        'cmark_type': {
            default: -> 4
            getFunc: 'CMarkType'
            enum: [arg for arg in *PPM2.DefaultCutiemarks]
            fix: (arg = 4) -> math.Clamp(tonumber(arg) or 4, PPM2.MIN_CMARK, PPM2.MAX_CMARK)
        }

        'cmark_url': {
            default: -> ''
            getFunc: 'CMarkURL'
            fix: (arg = '') ->
                arg = tostring(arg)
                if arg\find('^https?://')
                    return arg
                else
                    return ''
        }

        'eye_bg': {
            default: -> Color(255, 255, 255)
            getFunc: 'EyeBackground'
            fix: (arg = Color(255, 255, 255)) ->
                if type(arg) ~= 'table'
                    return Color(255, 255, 255)
                else
                    {:r, :g, :b, :a} = arg
                    if r and g and b and a
                        return Color(r, g, b, a)
                    else
                        return Color(255, 255, 255)
        }

        'eye_hole': {
            default: -> Color(0, 0, 0)
            getFunc: 'EyeHole'
            fix: (arg = Color(0, 0, 0)) ->
                if type(arg) ~= 'table'
                    return Color(0, 0, 0)
                else
                    {:r, :g, :b, :a} = arg
                    if r and g and b and a
                        return Color(r, g, b, a)
                    else
                        return Color(0, 0, 0)
        }

        'eye_iris1': {
            default: -> Color(200, 200, 200)
            getFunc: 'EyeIrisTop'
            fix: (arg = Color(200, 200, 200)) ->
                if type(arg) ~= 'table'
                    return Color(200, 200, 200)
                else
                    {:r, :g, :b, :a} = arg
                    if r and g and b and a
                        return Color(r, g, b, a)
                    else
                        return Color(200, 200, 200)
        }

        'eye_iris2': {
            default: -> Color(200, 200, 200)
            getFunc: 'EyeIrisBottom'
            fix: (arg = Color(200, 200, 200)) ->
                if type(arg) ~= 'table'
                    return Color(200, 200, 200)
                else
                    {:r, :g, :b, :a} = arg
                    if r and g and b and a
                        return Color(r, g, b, a)
                    else
                        return Color(200, 200, 200)
        }

        'eye_irisline1': {
            default: -> Color(255, 255, 255)
            getFunc: 'EyeIrisLine1'
            fix: (arg = Color(255, 255, 255)) ->
                if type(arg) ~= 'table'
                    return Color(255, 255, 255)
                else
                    {:r, :g, :b, :a} = arg
                    if r and g and b and a
                        return Color(r, g, b, a)
                    else
                        return Color(255, 255, 255)
        }

        'eye_irisline2': {
            default: -> Color(255, 255, 255)
            getFunc: 'EyeIrisLine2'
            fix: (arg = Color(255, 255, 255)) ->
                if type(arg) ~= 'table'
                    return Color(255, 255, 255)
                else
                    {:r, :g, :b, :a} = arg
                    if r and g and b and a
                        return Color(r, g, b, a)
                    else
                        return Color(255, 255, 255)
        }

        'body': {
            default: -> Color(255, 255, 255)
            getFunc: 'BodyColor'
            fix: (arg = Color(255, 255, 255)) ->
                if type(arg) ~= 'table'
                    return Color(255, 255, 255)
                else
                    {:r, :g, :b, :a} = arg
                    if r and g and b and a
                        return Color(r, g, b, a)
                    else
                        return Color(255, 255, 255)
        }

        'eye_lines': {
            default: -> true
            getFunc: 'EyeLines'
            fix: (arg = true) -> tobool(arg)
        }
    }

    for i = 1, 6
        @PONY_DATA["mane_color_#{i}"] = {
            default: -> Color(255, 255, 255)
            getFunc: "ManeColor#{i}"
            fix: (arg = Color(255, 255, 255)) ->
                if type(arg) ~= 'table'
                    return Color(255, 255, 255)
                else
                    {:r, :g, :b, :a} = arg
                    if r and g and b and a
                        return Color(r, g, b, a)
                    else
                        return Color(255, 255, 255)
        }

        @PONY_DATA["mane_detail_color_#{i}"] = {
            default: -> Color(255, 255, 255)
            getFunc: "ManeDetailColor#{i}"
            fix: (arg = Color(255, 255, 255)) ->
                if type(arg) ~= 'table'
                    return Color(255, 255, 255)
                else
                    {:r, :g, :b, :a} = arg
                    if r and g and b and a
                        return Color(r, g, b, a)
                    else
                        return Color(255, 255, 255)
        }

        @PONY_DATA["tail_detail_color_#{i}"] = {
            default: -> Color(255, 255, 255)
            getFunc: "TailDetailColor#{i}"
            fix: (arg = Color(255, 255, 255)) ->
                if type(arg) ~= 'table'
                    return Color(255, 255, 255)
                else
                    {:r, :g, :b, :a} = arg
                    if r and g and b and a
                        return Color(r, g, b, a)
                    else
                        return Color(255, 255, 255)
        }

        @PONY_DATA["tail_color_#{i}"] = {
            default: -> Color(255, 255, 255)
            getFunc: "TailColor#{i}"
            fix: (arg = Color(255, 255, 255)) ->
                if type(arg) ~= 'table'
                    return Color(255, 255, 255)
                else
                    {:r, :g, :b, :a} = arg
                    if r and g and b and a
                        return Color(r, g, b, a)
                    else
                        return Color(255, 255, 255)
        }

        -- Reserved - they can be accessed/used/changed, but they do not do anything
        @PONY_DATA["lower_mane_color_#{i}"] = {
            default: -> Color(255, 255, 255)
            getFunc: "LowerManeColor#{i}"
            fix: (arg = Color(255, 255, 255)) ->
                if type(arg) ~= 'table'
                    return Color(255, 255, 255)
                else
                    {:r, :g, :b, :a} = arg
                    if r and g and b and a
                        return Color(r, g, b, a)
                    else
                        return Color(255, 255, 255)
        }

        @PONY_DATA["upper_mane_color_#{i}"] = {
            default: -> Color(255, 255, 255)
            getFunc: "UpperManeColor#{i}"
            fix: (arg = Color(255, 255, 255)) ->
                if type(arg) ~= 'table'
                    return Color(255, 255, 255)
                else
                    {:r, :g, :b, :a} = arg
                    if r and g and b and a
                        return Color(r, g, b, a)
                    else
                        return Color(255, 255, 255)
        }
        ------

    @PONY_DATA_MAPPING = {getFunc\lower(), key for key, {:getFunc} in pairs @PONY_DATA}
    @PONY_DATA_MAPPING[key] = key for key, value in pairs @PONY_DATA

    for key, data in pairs @PONY_DATA
        continue unless data.enum
        data.enum = [arg\upper() for arg in *data.enum]
        data.enumMapping = {}
        data.enumMappingBackward = {}
        i = -1
        for enumVal in *data.enum
            i += 1
            data.enumMapping[i] = enumVal
            data.enumMappingBackward[enumVal] = i
    for key, {:getFunc, :fix, :enumMappingBackward, :enumMapping} in pairs @PONY_DATA
        @__base["Get#{getFunc}"] = => @dataTable[key]
        if enumMapping
            @__base["Get#{getFunc}Enum"] = => enumMapping[@dataTable[key]] or enumMapping[0] or @dataTable[key]
            @__base["GetEnum#{getFunc}"] = @__base["Get#{getFunc}Enum"]
		@__base["Set#{getFunc}"] = (val = defValue, ...) =>
            if type(val) == 'string' and enumMappingBackward
                newVal = enumMappingBackward[val\upper()]
                val = newVal if newVal
            newVal = fix(val)
			oldVal = @dataTable[key]
			@dataTable[key] = newVal
            @ValueChanges(key, oldVal, newVal, ...)

    new: (filename, data, readIfExists = true, force = false, doBackup = true) =>
        @SetFilename(filename)
        @valid = @isOpen
        @rawData = data
        @dataTable = {k, default() for k, {:default} in pairs @@PONY_DATA}
        if data
            @SetupData(data)
        elseif @exists and readIfExists
            @ReadFromDisk(force, doBackup)

    @ERR_MISSING_PARAMETER = 4
    @ERR_MISSING_CONTENT = 5
    SetupData: (data, doSave = false, force = false, doBackup = true) =>
        if doBackup or not force
            makeBackup = false
            for key, value in pairs data
                key = key\lower()
                map = @@PONY_DATA_MAPPING[key]
                if not map
                    return @@ERR_MISSING_PARAMETER if not force
                    makeBackup = true
                    break
                mapData = @@PONY_DATA[map]
                if mapData.enum
                    if type(value) == 'string' and not mapData.enumMappingBackward[value\upper()] or type(value) == 'number' and not mapData.enumMapping[value]
                        return @@ERR_MISSING_CONTENT if not force
                        makeBackup = true
                        break
            if doBackup and makeBackup and @exists
                bkName = "#{@@DATA_DIR_BACKUP}#{filename}_bak_#{os.date('%S_%M_%H.%d.%m.%Y', os.time())}.txt"
                fRead = file.Read(@fpath, 'DATA')
                file.Write(@fpathBackup, fRead)

        for key, value in pairs data
            key = key\lower()
            map = @@PONY_DATA_MAPPING[key]
            continue unless map
            mapData = @@PONY_DATA[map]
            if mapData.enum and type(value) == 'string'
                @dataTable[key] = mapData.fix(mapData.enumMappingBackward[value\upper()])
            else
                @dataTable[key] = mapData.fix(value)
    ValueChanges: (key, oldVal, newVal, saveNow = @exists) =>
        if @nwObj
            {:getFunc} = @@PONY_DATA[key]
            @nwObj["Set#{getFunc}"](@nwObj, newVal)
        @Save() if saveNow
    SetFilename: (filename) =>
        @filename = filename
        @filenameFull = "#{filename}.txt"
        @fpath = "#{@@DATA_DIR}#{filename}.txt"
        @fpathBackup = "#{@@DATA_DIR}#{filename}.bak.txt"
        @fpathFull = "data/#{@@DATA_DIR}#{filename}.txt"
        @isOpen = @filename ~= nil
        @exists = file.Exists(@fpath, 'DATA')
        return @exists
    SetNetworkData: (nwObj) => @nwObj = nwObj
    IsValid: => @valid
    Exists: => @exists
    FileExists: => @exists
    IsExists: => @exists
    GetFileName: => @filename
    GetFileNameFull: => @filenameFull
    GetFilePath: => @fpath
    GetFullFilePath: => @fpathFull
    SerealizeValue: (valID = '') =>
        map = @@PONY_DATA[valID]
        return unless map
        val = @dataTable[valID]
        if map.enum
            return map.enumMapping[val] or map.enumMapping[map.default()]
        elseif map.serealize
            return map.serealize(val)
        else
            return val
    Serealize: (prettyPrint = true) =>
        serTab = {key, @SerealizeValue(key) for key, val in pairs @dataTable}
        util.TableToJSON(serTab, prettyPrint)
    GetAsNetworked: => {getFunc, @dataTable[k] for k, {:getFunc} in pairs @@PONY_DATA}

    @READ_SUCCESS = 0
    @ERR_FILE_NOT_EXISTS = 1
    @ERR_FILE_EMPTY = 2
    @ERR_FILE_CORRUPT = 3
    ReadFromDisk: (force = false, doBackup = true) =>
        return @@ERR_FILE_NOT_EXISTS unless @exists
        fRead = file.Read(@fpath, 'DATA')
        return @@ERR_FILE_EMPTY if not fRead or fRead == ''
        readTable = util.JSONToTable(fRead)
        return @@ERR_FILE_CORRUPT unless readTable
        @SetupData(readTable, false, force)
        return @SetupData(readTable, false, force, doBackup) or @@READ_SUCCESS
    Save: (doBackup = true) =>
        if doBackup and @exists
            fRead = file.Read(@fpath, 'DATA')
            file.Write(@fpathBackup, fRead)
        fCreate = @Serealize()
        file.Write(@fpath, fCreate)
        @exists = true

PPM2.PonyDataInstance = PonyDataInstance

PPM2.MainDataInstance = nil
PPM2.GetMainData = ->
    if not PPM2.MainDataInstance
        PPM2.MainDataInstance = PonyDataInstance('_current', nil, true, true)
        if not PPM2.MainDataInstance\FileExists()
            PPM2.MainDataInstance\Save()
    return PPM2.MainDataInstance


Just a slighter larger value than original

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

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

USE_NEW_HULL = CreateConVar('ppm2_sv_newhull', '1', {FCVAR_ARCHIVE, FCVAR_NOTIFY, FCVAR_REPLICATED}, 'Use proper collision box for ponies. Slightly affects jump mechanics. When disabled, unexpected behaviour could happen.')
ALLOW_TO_MODIFY_SCALE = PPM2.ALLOW_TO_MODIFY_SCALE

class PonySizeController extends PPM2.ControllerChildren
	@AVALIABLE_CONTROLLERS = {}
	@MODELS = {'models/ppm/player_default_base.mdl', 'models/ppm/player_default_base_nj.mdl', 'models/cppm/player_default_base.mdl', 'models/cppm/player_default_base_nj.mdl'}

	@NECK_BONE_1 = 4
	@NECK_BONE_2 = 5
	@NECK_BONE_3 = 6
	@NECK_BONE_4 = 7

	@LEGS_BONE_ROOT = 0

	@LEGS_FRONT_1 = 23
	@LEGS_FRONT_2 = 29

	@LEGS_FRONT_3 = 21
	@LEGS_FRONT_4 = 27

	@LEGS_FRONT_5 = 20
	@LEGS_FRONT_6 = 26

	@LEGS_BEHIND_1_1 = 14
	@LEGS_BEHIND_2_1 = 16
	@LEGS_BEHIND_3_1 = 15

	@LEGS_BEHIND_1_2 = 9
	@LEGS_BEHIND_2_2 = 11
	@LEGS_BEHIND_3_2 = 10

	@NEXT_OBJ_ID = 0

	new: (controller) =>
		@isValid = true
		@ent = controller.ent
		@entID = controller.entID
		@controller = controller
		@objID = @@NEXT_OBJ_ID
		@@NEXT_OBJ_ID += 1
		@lastPAC3BoneReset = 0
		PPM2.DebugPrint('Created new size controller for ', @ent, ' as part of ', controller, '; internal ID is ', @objID)

	__tostring: => "[#{@@__name}:#{@objID}|#{@ent}]"
	IsValid: => @isValid
	GetData: => @controller
	GetEntity: => @ent
	GetEntityID: => @entID
	GetDataID: => @entID
	IsNetworked: => @controller\IsNetworked()
	AllowResize: => not @controller\IsNetworked() or ALLOW_TO_MODIFY_SCALE\GetBool()

	@STEP_SIZE = 20
	@PONY_HULL = 17
	@HULL_MINS = Vector(-@PONY_HULL, -@PONY_HULL, 0)
	@HULL_MAXS = Vector(@PONY_HULL, @PONY_HULL, 72 * PPM2.PONY_HEIGHT_MODIFIER)
	@HULL_MAXS_DUCK = Vector(@PONY_HULL, @PONY_HULL, 36 * PPM2.PONY_HEIGHT_MODIFIER_DUCK_HULL)

	@DEFAULT_HULL_MINS = Vector(-16, -16, 0)
	@DEFAULT_HULL_MAXS = Vector(16, 16, 72)
	@DEFAULT_HULL_MAXS_DUCK = Vector(16, 16, 36)
	@DEF_SCALE = Vector(1, 1, 1)

	DataChanges: (state) =>
		return if not IsValid(@ent)
		return if not @ent\IsPony()
		if state\GetKey() == 'PonySize'
			@ModifyScale()

		if state\GetKey() == 'NeckSize'
			@ModifyNeck()
			@ModifyViewOffset()

		if state\GetKey() == 'LegsSize'
			@ModifyLegs()
			@ModifyHull()
			@ModifyViewOffset()

	ResetViewOffset: (ent = @ent) =>
		ent\SetViewOffset(PPM2.PLAYER_VIEW_OFFSET_ORIGINAL) if ent.SetViewOffset
		ent\SetViewOffsetDucked(PPM2.PLAYER_VIEW_OFFSET_DUCK_ORIGINAL) if ent.SetViewOffsetDucked

	ResetHulls: (ent = @ent) =>
		ent\ResetHull() if ent.ResetHull
		ent\SetStepSize(@@STEP_SIZE) if ent.SetStepSize
		ent.__ppm2_modified_hull = false

	ResetJumpHeight: (ent = @ent) =>
		return if CLIENT
		return if not ent.SetJumpPower
		return if not ent.__ppm2_modified_jump
		ent\SetJumpPower(ent\GetJumpPower() / PPM2.PONY_JUMP_MODIFIER)
		ent.__ppm2_modified_jump = false

	ResetDrawMatrix: (ent = @ent) =>
		return if SERVER
		mat = Matrix()
		mat\Scale(@@DEF_SCALE)
		ent\EnableMatrix('RenderMultiply', mat)

	ResetScale: (ent = @ent) =>
		return if not IsValid(ent)

		if USE_NEW_HULL\GetBool() or ent.__ppm2_modified_hull
			@ResetHulls(ent)
			@ResetJumpHeight(ent)

		@ResetViewOffset(ent)
		@ResetDrawMatrix(ent)
		@ResetNeck(ent)
		@ResetLegs(ent)

	ResetNeck: (ent = @ent) =>
		return if not CLIENT
		return if not IsValid(@ent)
		with ent
			\ManipulateBoneScale(@@NECK_BONE_1, Vector(1, 1, 1))
			\ManipulateBoneScale(@@NECK_BONE_2, Vector(1, 1, 1))
			\ManipulateBoneScale(@@NECK_BONE_3, Vector(1, 1, 1))
			\ManipulateBoneScale(@@NECK_BONE_4, Vector(1, 1, 1))
			\ManipulateBoneAngles(@@NECK_BONE_1, Angle(0, 0, 0))
			\ManipulateBoneAngles(@@NECK_BONE_2, Angle(0, 0, 0))
			\ManipulateBoneAngles(@@NECK_BONE_3, Angle(0, 0, 0))
			\ManipulateBoneAngles(@@NECK_BONE_4, Angle(0, 0, 0))
			\ManipulateBonePosition(@@NECK_BONE_1, Vector(0, 0, 0))
			\ManipulateBonePosition(@@NECK_BONE_2, Vector(0, 0, 0))
			\ManipulateBonePosition(@@NECK_BONE_3, Vector(0, 0, 0))
			\ManipulateBonePosition(@@NECK_BONE_4, Vector(0, 0, 0))

	ResetLegs: (ent = @ent) =>
		return if not CLIENT
		return if not IsValid(ent)

		vec1 = Vector(1, 1, 1)
		vec2 = Vector(0, 0, 0)
		ang = Angle(0, 0, 0)

		with ent
			\ManipulateBoneScale(@@LEGS_BONE_ROOT, vec1)
			\ManipulateBoneScale(@@LEGS_FRONT_1, vec1)
			\ManipulateBoneScale(@@LEGS_FRONT_2, vec1)
			\ManipulateBoneScale(@@LEGS_BEHIND_1_1, vec1)
			\ManipulateBoneScale(@@LEGS_BEHIND_2_1, vec1)
			\ManipulateBoneScale(@@LEGS_BEHIND_3_1, vec1)
			\ManipulateBoneScale(@@LEGS_BEHIND_1_2, vec1)
			\ManipulateBoneScale(@@LEGS_BEHIND_2_2, vec1)
			\ManipulateBoneScale(@@LEGS_BEHIND_3_2, vec1)

			\ManipulateBoneAngles(@@LEGS_BONE_ROOT, ang)
			\ManipulateBoneAngles(@@LEGS_FRONT_1, ang)
			\ManipulateBoneAngles(@@LEGS_FRONT_2, ang)
			\ManipulateBoneAngles(@@LEGS_BEHIND_1_1, ang)
			\ManipulateBoneAngles(@@LEGS_BEHIND_2_1, ang)
			\ManipulateBoneAngles(@@LEGS_BEHIND_3_1, ang)
			\ManipulateBoneAngles(@@LEGS_BEHIND_1_2, ang)
			\ManipulateBoneAngles(@@LEGS_BEHIND_2_2, ang)
			\ManipulateBoneAngles(@@LEGS_BEHIND_3_2, ang)

			\ManipulateBonePosition(@@LEGS_BONE_ROOT, vec2)
			\ManipulateBonePosition(@@LEGS_FRONT_1, vec2)
			\ManipulateBonePosition(@@LEGS_FRONT_2, vec2)
			\ManipulateBonePosition(@@LEGS_BEHIND_1_1, vec2)
			\ManipulateBonePosition(@@LEGS_BEHIND_2_1, vec2)
			\ManipulateBonePosition(@@LEGS_BEHIND_3_1, vec2)
			\ManipulateBonePosition(@@LEGS_BEHIND_1_2, vec2)
			\ManipulateBonePosition(@@LEGS_BEHIND_2_2, vec2)
			\ManipulateBonePosition(@@LEGS_BEHIND_3_2, vec2)

	Remove: => @ResetScale()
	Reset: =>
		@ResetScale()
		@ResetNeck()
		@ResetLegs()
		@ModifyScale()

	GetLegsSize: => @GetData()\GetLegsSize()
	GetLegsScale: => @GetData()\GetLegsSize()
	GetNeckSize: => @GetData()\GetNeckSize()
	GetNeckScale: => @GetData()\GetNeckSize()
	GetPonySize: => @GetData()\GetPonySize()
	GetPonyScale: => @GetData()\GetPonySize()

	PlayerDeath: =>
		@ResetScale()
		@ResetNeck()
		@ResetLegs()
	PlayerRespawn: =>
		@ResetScale()
		@ModifyScale()

	SlowUpdate: =>
		@ModifyScale()

	ModifyHull: (ent = @ent) =>
		ent.__ppm2_modified_hull = true
		size = @GetPonySize()
		legssize = @GetLegsModifier()

		HULL_MINS = Vector(@@HULL_MINS)
		HULL_MAXS = Vector(@@HULL_MAXS)
		HULL_MAXS_DUCK = Vector(@@HULL_MAXS_DUCK)

		if @AllowResize()
			HULL_MINS *= size
			HULL_MAXS *= size
			HULL_MAXS_DUCK *= size

			HULL_MINS.z *= legssize
			HULL_MAXS.z *= legssize
			HULL_MAXS_DUCK.z *= legssize

		with ent
			\SetHull(HULL_MINS, HULL_MAXS) if .SetHull
			\SetHullDuck(HULL_MINS, HULL_MAXS_DUCK) if .SetHullDuck
			\SetStepSize(@@STEP_SIZE * size * @GetLegsModifier(1.2)) if .SetStepSize

	ModifyJumpHeight: (ent = @ent) =>
		return if CLIENT
		return if not @ent.SetJumpPower
		return if ent.__ppm2_modified_jump
		ent\SetJumpPower(ent\GetJumpPower() * PPM2.PONY_JUMP_MODIFIER)
		ent.__ppm2_modified_jump = true

	GetLegsModifier: (mult = 0.4) =>
		if @AllowResize()
			1 + (@GetLegsSize() - 1) * mult
		else
			1

	ModifyViewOffset: (ent = @ent) =>
		size = @GetPonySize()
		necksize = 1 + (@GetNeckSize() - 1) * .3
		legssize = @GetLegsModifier()

		PLAYER_VIEW_OFFSET = Vector(PPM2.PLAYER_VIEW_OFFSET)
		PLAYER_VIEW_OFFSET_DUCK = Vector(PPM2.PLAYER_VIEW_OFFSET_DUCK)

		if @AllowResize()
			PLAYER_VIEW_OFFSET *= size * necksize
			PLAYER_VIEW_OFFSET_DUCK *= size * necksize

			PLAYER_VIEW_OFFSET.z *= legssize
			PLAYER_VIEW_OFFSET_DUCK.z *= legssize

		ent\SetViewOffset(PLAYER_VIEW_OFFSET) if ent.SetViewOffset
		ent\SetViewOffsetDucked(PLAYER_VIEW_OFFSET_DUCK) if ent.SetViewOffsetDucked

	ModifyDrawMatrix: (ent = @ent) =>
		return if SERVER
		return if not @AllowResize()
		return if ent.RenderOverride -- PAC3 and other stuff that can change this value
		mat = Matrix()
		mat\Scale(@@DEF_SCALE * @GetPonySize())
		ent\EnableMatrix('RenderMultiply', mat)

	ModifyScale: (ent = @ent) =>
		return if not IsValid(ent)
		return if not ent\IsPony()
		return if ent.Alive and not ent\Alive()

		if USE_NEW_HULL\GetBool()
			@ModifyHull(ent)
			@ModifyJumpHeight(ent)

		@ModifyViewOffset(ent)
		@ModifyDrawMatrix(ent)
		if @lastPAC3BoneReset < RealTime()
			@ModifyNeck(ent)
			@ModifyLegs(ent)

	ModifyNeck: (ent = @ent) =>
		return if SERVER
		return if not IsValid(ent)
		return if not @AllowResize()
		size = (@GetNeckSize() - 1) * 3
		vec = Vector(size, -size, 0)

		boneAnimTable = ent.pac_boneanim and ent.pac_boneanim.positions or {}
		emptyVector = Vector(0, 0, 0)

		with ent
			\ManipulateBonePosition(@@NECK_BONE_1, vec + (boneAnimTable[@@NECK_BONE_1] or emptyVector))
			\ManipulateBonePosition(@@NECK_BONE_2, vec + (boneAnimTable[@@NECK_BONE_2] or emptyVector))
			\ManipulateBonePosition(@@NECK_BONE_3, vec + (boneAnimTable[@@NECK_BONE_3] or emptyVector))
			\ManipulateBonePosition(@@NECK_BONE_4, vec + (boneAnimTable[@@NECK_BONE_4] or emptyVector))

	ModifyLegs: (ent = @ent) =>
		return if SERVER
		return if not IsValid(ent)
		return if not @AllowResize()
		realSizeModify = @GetLegsSize() - 1
		size = realSizeModify * 3

		boneAnimTable = ent.pac_boneanim and ent.pac_boneanim.positions or {}
		emptyVector = Vector(0, 0, 0)

		with ent
			\ManipulateBonePosition(@@LEGS_BONE_ROOT, Vector(0, 0, size * 5) + (boneAnimTable[@@LEGS_BONE_ROOT] or emptyVector))
			\ManipulateBonePosition(@@LEGS_FRONT_1, Vector(size * 1.5, 0, 0) + (boneAnimTable[@@LEGS_FRONT_1] or emptyVector))
			\ManipulateBonePosition(@@LEGS_FRONT_2, Vector(size * 1.5, 0, 0) + (boneAnimTable[@@LEGS_FRONT_2] or emptyVector))

			\ManipulateBonePosition(@@LEGS_FRONT_3, Vector(size, 0, 0) + (boneAnimTable[@@LEGS_FRONT_3] or emptyVector))
			\ManipulateBonePosition(@@LEGS_FRONT_4, Vector(size, 0, 0) + (boneAnimTable[@@LEGS_FRONT_4] or emptyVector))

			\ManipulateBonePosition(@@LEGS_FRONT_5, Vector(size, size, 0) + (boneAnimTable[@@LEGS_FRONT_5] or emptyVector))
			\ManipulateBonePosition(@@LEGS_FRONT_6, Vector(size, size, 0) + (boneAnimTable[@@LEGS_FRONT_6] or emptyVector))

			\ManipulateBonePosition(@@LEGS_BEHIND_1_1, Vector(size, -size * 0.5, 0) + (boneAnimTable[@@LEGS_BEHIND_1_1] or emptyVector))
			\ManipulateBonePosition(@@LEGS_BEHIND_1_2, Vector(size, -size * 0.5, 0) + (boneAnimTable[@@LEGS_BEHIND_1_2] or emptyVector))

			\ManipulateBonePosition(@@LEGS_BEHIND_2_1, Vector(size, 0, 0) + (boneAnimTable[@@LEGS_BEHIND_2_1] or emptyVector))
			\ManipulateBonePosition(@@LEGS_BEHIND_2_2, Vector(size, 0, 0) + (boneAnimTable[@@LEGS_BEHIND_2_2] or emptyVector))

			\ManipulateBonePosition(@@LEGS_BEHIND_3_1, Vector(size * 2, 0, 0) + (boneAnimTable[@@LEGS_BEHIND_3_1] or emptyVector))
			\ManipulateBonePosition(@@LEGS_BEHIND_3_2, Vector(size * 2, 0, 0) + (boneAnimTable[@@LEGS_BEHIND_3_2] or emptyVector))

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

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

	@NECK_BONE_1 = 26
	@NECK_BONE_2 = 27
	@NECK_BONE_3 = 28
	@NECK_BONE_4 = 29

	@LEGS_FRONT_1 = 19
	@LEGS_FRONT_2 = 25

	@LEGS_FRONT_3 = 17
	@LEGS_FRONT_4 = 23

	@LEGS_FRONT_5 = 16
	@LEGS_FRONT_6 = 22

	@LEGS_BEHIND_1_1 = 2
	@LEGS_BEHIND_1_2 = 7

	@LEGS_BEHIND_2_1 = 4
	@LEGS_BEHIND_2_2 = 9

	@LEGS_BEHIND_3_1 = 3
	@LEGS_BEHIND_3_2 = 8

	new: (...) =>
		super(...)

if CLIENT
	hook.Add 'PPM2.SetupBones', 'PPM2.Size', (ent, data) ->
		if sizes = data\GetSizeController()
			sizes.ent = ent
			sizes\ModifyNeck()
			sizes\ModifyLegs()
			sizes.lastPAC3BoneReset = RealTime() + 1

ppm2_sv_allow_resize = ->
	for ply in *player.GetAll()
		if data = ply\GetPonyData()
			if scale = data\GetSizeController()
				scale\Reset()

cvars.AddChangeCallback 'ppm2_sv_allow_resize', ppm2_sv_allow_resize, 'PPM2.Scale'

PPM2.GetSizeController = (model = 'models/ppm/player_default_base.mdl') -> PonySizeController.AVALIABLE_CONTROLLERS[model\lower()] or PonySizeController

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

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

USE_NEW_HULL = CreateConVar('ppm2_sv_newhull', '1', {FCVAR_ARCHIVE, FCVAR_NOTIFY, FCVAR_REPLICATED}, 'Use proper collision box for ponies. Slightly affects jump mechanics. When disabled, unexpected behaviour could happen.')
ALLOW_TO_MODIFY_SCALE = PPM2.ALLOW_TO_MODIFY_SCALE

class PonySizeController extends PPM2.ControllerChildren
	@AVALIABLE_CONTROLLERS = {}
	@MODELS = {'models/ppm/player_default_base.mdl', 'models/ppm/player_default_base_nj.mdl', 'models/cppm/player_default_base.mdl', 'models/cppm/player_default_base_nj.mdl'}

	@NECK_BONE_1 = 4
	@NECK_BONE_2 = 5
	@NECK_BONE_3 = 6
	@NECK_BONE_4 = 7

	@LEGS_BONE_ROOT = 0

	@LEGS_FRONT_1 = 23
	@LEGS_FRONT_2 = 29

	@LEGS_FRONT_3 = 21
	@LEGS_FRONT_4 = 27

	@LEGS_FRONT_5 = 20
	@LEGS_FRONT_6 = 26

	@LEGS_BEHIND_1_1 = 14
	@LEGS_BEHIND_2_1 = 16
	@LEGS_BEHIND_3_1 = 15

	@LEGS_BEHIND_1_2 = 9
	@LEGS_BEHIND_2_2 = 11
	@LEGS_BEHIND_3_2 = 10

	@NEXT_OBJ_ID = 0

	new: (controller) =>
		@isValid = true
		@ent = controller.ent
		@entID = controller.entID
		@controller = controller
		@objID = @@NEXT_OBJ_ID
		@@NEXT_OBJ_ID += 1
		@lastPAC3BoneReset = 0
		PPM2.DebugPrint('Created new size controller for ', @ent, ' as part of ', controller, '; internal ID is ', @objID)

	__tostring: => "[#{@@__name}:#{@objID}|#{@ent}]"
	IsValid: => @isValid
	GetData: => @controller
	GetEntity: => @ent
	GetEntityID: => @entID
	GetDataID: => @entID
	IsNetworked: => @controller\IsNetworked()
	AllowResize: => not @controller\IsNetworked() or ALLOW_TO_MODIFY_SCALE\GetBool()

	@STEP_SIZE = 18
	@PONY_HULL = 19
	@HULL_MINS = Vector(-@PONY_HULL, -@PONY_HULL, 0)
	@HULL_MAXS = Vector(@PONY_HULL, @PONY_HULL, 72 * PPM2.PONY_HEIGHT_MODIFIER)
	@HULL_MAXS_DUCK = Vector(@PONY_HULL, @PONY_HULL, 36 * PPM2.PONY_HEIGHT_MODIFIER_DUCK_HULL)

	@DEFAULT_HULL_MINS = Vector(-16, -16, 0)
	@DEFAULT_HULL_MAXS = Vector(16, 16, 72)
	@DEFAULT_HULL_MAXS_DUCK = Vector(16, 16, 36)
	@DEF_SCALE = Vector(1, 1, 1)

	DataChanges: (state) =>
		return if not IsValid(@ent)
		return if not @ent\IsPony()
		if state\GetKey() == 'PonySize'
			@ModifyScale()

		if state\GetKey() == 'NeckSize'
			@ModifyNeck()
			@ModifyViewOffset()

		if state\GetKey() == 'LegsSize'
			@ModifyLegs()
			@ModifyHull()
			@ModifyViewOffset()

	ResetViewOffset: (ent = @ent) =>
		ent\SetViewOffset(PPM2.PLAYER_VIEW_OFFSET_ORIGINAL) if ent.SetViewOffset
		ent\SetViewOffsetDucked(PPM2.PLAYER_VIEW_OFFSET_DUCK_ORIGINAL) if ent.SetViewOffsetDucked

	ResetHulls: (ent = @ent) =>
		ent\ResetHull() if ent.ResetHull
		ent\SetStepSize(@@STEP_SIZE) if ent.SetStepSize
		ent.__ppm2_modified_hull = false

	ResetJumpHeight: (ent = @ent) =>
		return if CLIENT
		return if not ent.SetJumpPower
		return if not ent.__ppm2_modified_jump
		ent\SetJumpPower(ent\GetJumpPower() / PPM2.PONY_JUMP_MODIFIER)
		ent.__ppm2_modified_jump = false

	ResetDrawMatrix: (ent = @ent) =>
		return if SERVER
		mat = Matrix()
		mat\Scale(@@DEF_SCALE)
		ent\EnableMatrix('RenderMultiply', mat)

	ResetScale: (ent = @ent) =>
		return if not IsValid(ent)

		if USE_NEW_HULL\GetBool() or ent.__ppm2_modified_hull
			@ResetHulls(ent)
			@ResetJumpHeight(ent)

		@ResetViewOffset(ent)
		@ResetDrawMatrix(ent)
		@ResetNeck(ent)
		@ResetLegs(ent)

	ResetNeck: (ent = @ent) =>
		return if not CLIENT
		return if not IsValid(@ent)
		with ent
			\ManipulateBoneScale(@@NECK_BONE_1, Vector(1, 1, 1))
			\ManipulateBoneScale(@@NECK_BONE_2, Vector(1, 1, 1))
			\ManipulateBoneScale(@@NECK_BONE_3, Vector(1, 1, 1))
			\ManipulateBoneScale(@@NECK_BONE_4, Vector(1, 1, 1))
			\ManipulateBoneAngles(@@NECK_BONE_1, Angle(0, 0, 0))
			\ManipulateBoneAngles(@@NECK_BONE_2, Angle(0, 0, 0))
			\ManipulateBoneAngles(@@NECK_BONE_3, Angle(0, 0, 0))
			\ManipulateBoneAngles(@@NECK_BONE_4, Angle(0, 0, 0))
			\ManipulateBonePosition(@@NECK_BONE_1, Vector(0, 0, 0))
			\ManipulateBonePosition(@@NECK_BONE_2, Vector(0, 0, 0))
			\ManipulateBonePosition(@@NECK_BONE_3, Vector(0, 0, 0))
			\ManipulateBonePosition(@@NECK_BONE_4, Vector(0, 0, 0))

	ResetLegs: (ent = @ent) =>
		return if not CLIENT
		return if not IsValid(ent)

		vec1 = Vector(1, 1, 1)
		vec2 = Vector(0, 0, 0)
		ang = Angle(0, 0, 0)

		with ent
			\ManipulateBoneScale(@@LEGS_BONE_ROOT, vec1)
			\ManipulateBoneScale(@@LEGS_FRONT_1, vec1)
			\ManipulateBoneScale(@@LEGS_FRONT_2, vec1)
			\ManipulateBoneScale(@@LEGS_BEHIND_1_1, vec1)
			\ManipulateBoneScale(@@LEGS_BEHIND_2_1, vec1)
			\ManipulateBoneScale(@@LEGS_BEHIND_3_1, vec1)
			\ManipulateBoneScale(@@LEGS_BEHIND_1_2, vec1)
			\ManipulateBoneScale(@@LEGS_BEHIND_2_2, vec1)
			\ManipulateBoneScale(@@LEGS_BEHIND_3_2, vec1)

			\ManipulateBoneAngles(@@LEGS_BONE_ROOT, ang)
			\ManipulateBoneAngles(@@LEGS_FRONT_1, ang)
			\ManipulateBoneAngles(@@LEGS_FRONT_2, ang)
			\ManipulateBoneAngles(@@LEGS_BEHIND_1_1, ang)
			\ManipulateBoneAngles(@@LEGS_BEHIND_2_1, ang)
			\ManipulateBoneAngles(@@LEGS_BEHIND_3_1, ang)
			\ManipulateBoneAngles(@@LEGS_BEHIND_1_2, ang)
			\ManipulateBoneAngles(@@LEGS_BEHIND_2_2, ang)
			\ManipulateBoneAngles(@@LEGS_BEHIND_3_2, ang)

			\ManipulateBonePosition(@@LEGS_BONE_ROOT, vec2)
			\ManipulateBonePosition(@@LEGS_FRONT_1, vec2)
			\ManipulateBonePosition(@@LEGS_FRONT_2, vec2)
			\ManipulateBonePosition(@@LEGS_BEHIND_1_1, vec2)
			\ManipulateBonePosition(@@LEGS_BEHIND_2_1, vec2)
			\ManipulateBonePosition(@@LEGS_BEHIND_3_1, vec2)
			\ManipulateBonePosition(@@LEGS_BEHIND_1_2, vec2)
			\ManipulateBonePosition(@@LEGS_BEHIND_2_2, vec2)
			\ManipulateBonePosition(@@LEGS_BEHIND_3_2, vec2)

	Remove: => @ResetScale()
	Reset: =>
		@ResetScale()
		@ResetNeck()
		@ResetLegs()
		@ModifyScale()

	GetLegsSize: => @GetData()\GetLegsSize()
	GetLegsScale: => @GetData()\GetLegsSize()
	GetNeckSize: => @GetData()\GetNeckSize()
	GetNeckScale: => @GetData()\GetNeckSize()
	GetPonySize: => @GetData()\GetPonySize()
	GetPonyScale: => @GetData()\GetPonySize()

	PlayerDeath: =>
		@ResetScale()
		@ResetNeck()
		@ResetLegs()
	PlayerRespawn: =>
		@ResetScale()
		@ModifyScale()

	SlowUpdate: =>
		@ModifyScale()

	ModifyHull: (ent = @ent) =>
		ent.__ppm2_modified_hull = true
		size = @GetPonySize()
		legssize = @GetLegsModifier()

		HULL_MINS = Vector(@@HULL_MINS)
		HULL_MAXS = Vector(@@HULL_MAXS)
		HULL_MAXS_DUCK = Vector(@@HULL_MAXS_DUCK)

		if @AllowResize()
			HULL_MINS *= size
			HULL_MAXS *= size
			HULL_MAXS_DUCK *= size

			HULL_MINS.z *= legssize
			HULL_MAXS.z *= legssize
			HULL_MAXS_DUCK.z *= legssize

		with ent
			\SetHull(HULL_MINS, HULL_MAXS) if .SetHull
			\SetHullDuck(HULL_MINS, HULL_MAXS_DUCK) if .SetHullDuck
			\SetStepSize(@@STEP_SIZE * size * @GetLegsModifier(1.2)) if .SetStepSize

	ModifyJumpHeight: (ent = @ent) =>
		return if CLIENT
		return if not @ent.SetJumpPower
		return if ent.__ppm2_modified_jump
		ent\SetJumpPower(ent\GetJumpPower() * PPM2.PONY_JUMP_MODIFIER)
		ent.__ppm2_modified_jump = true

	GetLegsModifier: (mult = 0.4) =>
		if @AllowResize()
			1 + (@GetLegsSize() - 1) * mult
		else
			1

	ModifyViewOffset: (ent = @ent) =>
		size = @GetPonySize()
		necksize = 1 + (@GetNeckSize() - 1) * .3
		legssize = @GetLegsModifier()

		PLAYER_VIEW_OFFSET = Vector(PPM2.PLAYER_VIEW_OFFSET)
		PLAYER_VIEW_OFFSET_DUCK = Vector(PPM2.PLAYER_VIEW_OFFSET_DUCK)

		if @AllowResize()
			PLAYER_VIEW_OFFSET *= size * necksize
			PLAYER_VIEW_OFFSET_DUCK *= size * necksize

			PLAYER_VIEW_OFFSET.z *= legssize
			PLAYER_VIEW_OFFSET_DUCK.z *= legssize

		ent\SetViewOffset(PLAYER_VIEW_OFFSET) if ent.SetViewOffset
		ent\SetViewOffsetDucked(PLAYER_VIEW_OFFSET_DUCK) if ent.SetViewOffsetDucked

	ModifyDrawMatrix: (ent = @ent) =>
		return if SERVER
		return if not @AllowResize()
		return if ent.RenderOverride -- PAC3 and other stuff that can change this value
		mat = Matrix()
		mat\Scale(@@DEF_SCALE * @GetPonySize())
		ent\EnableMatrix('RenderMultiply', mat)

	ModifyScale: (ent = @ent) =>
		return if not IsValid(ent)
		return if not ent\IsPony()
		return if ent.Alive and not ent\Alive()

		if USE_NEW_HULL\GetBool()
			@ModifyHull(ent)
			@ModifyJumpHeight(ent)

		@ModifyViewOffset(ent)
		@ModifyDrawMatrix(ent)
		if @lastPAC3BoneReset < RealTime()
			@ModifyNeck(ent)
			@ModifyLegs(ent)

	ModifyNeck: (ent = @ent) =>
		return if SERVER
		return if not IsValid(ent)
		return if not @AllowResize()
		size = (@GetNeckSize() - 1) * 3
		vec = Vector(size, -size, 0)

		boneAnimTable = ent.pac_boneanim and ent.pac_boneanim.positions or {}
		emptyVector = Vector(0, 0, 0)

		with ent
			\ManipulateBonePosition(@@NECK_BONE_1, vec + (boneAnimTable[@@NECK_BONE_1] or emptyVector))
			\ManipulateBonePosition(@@NECK_BONE_2, vec + (boneAnimTable[@@NECK_BONE_2] or emptyVector))
			\ManipulateBonePosition(@@NECK_BONE_3, vec + (boneAnimTable[@@NECK_BONE_3] or emptyVector))
			\ManipulateBonePosition(@@NECK_BONE_4, vec + (boneAnimTable[@@NECK_BONE_4] or emptyVector))

	ModifyLegs: (ent = @ent) =>
		return if SERVER
		return if not IsValid(ent)
		return if not @AllowResize()
		realSizeModify = @GetLegsSize() - 1
		size = realSizeModify * 3

		boneAnimTable = ent.pac_boneanim and ent.pac_boneanim.positions or {}
		emptyVector = Vector(0, 0, 0)

		with ent
			\ManipulateBonePosition(@@LEGS_BONE_ROOT, Vector(0, 0, size * 5) + (boneAnimTable[@@LEGS_BONE_ROOT] or emptyVector))
			\ManipulateBonePosition(@@LEGS_FRONT_1, Vector(size * 1.5, 0, 0) + (boneAnimTable[@@LEGS_FRONT_1] or emptyVector))
			\ManipulateBonePosition(@@LEGS_FRONT_2, Vector(size * 1.5, 0, 0) + (boneAnimTable[@@LEGS_FRONT_2] or emptyVector))

			\ManipulateBonePosition(@@LEGS_FRONT_3, Vector(size, 0, 0) + (boneAnimTable[@@LEGS_FRONT_3] or emptyVector))
			\ManipulateBonePosition(@@LEGS_FRONT_4, Vector(size, 0, 0) + (boneAnimTable[@@LEGS_FRONT_4] or emptyVector))

			\ManipulateBonePosition(@@LEGS_FRONT_5, Vector(size, size, 0) + (boneAnimTable[@@LEGS_FRONT_5] or emptyVector))
			\ManipulateBonePosition(@@LEGS_FRONT_6, Vector(size, size, 0) + (boneAnimTable[@@LEGS_FRONT_6] or emptyVector))

			\ManipulateBonePosition(@@LEGS_BEHIND_1_1, Vector(size, -size * 0.5, 0) + (boneAnimTable[@@LEGS_BEHIND_1_1] or emptyVector))
			\ManipulateBonePosition(@@LEGS_BEHIND_1_2, Vector(size, -size * 0.5, 0) + (boneAnimTable[@@LEGS_BEHIND_1_2] or emptyVector))

			\ManipulateBonePosition(@@LEGS_BEHIND_2_1, Vector(size, 0, 0) + (boneAnimTable[@@LEGS_BEHIND_2_1] or emptyVector))
			\ManipulateBonePosition(@@LEGS_BEHIND_2_2, Vector(size, 0, 0) + (boneAnimTable[@@LEGS_BEHIND_2_2] or emptyVector))

			\ManipulateBonePosition(@@LEGS_BEHIND_3_1, Vector(size * 2, 0, 0) + (boneAnimTable[@@LEGS_BEHIND_3_1] or emptyVector))
			\ManipulateBonePosition(@@LEGS_BEHIND_3_2, Vector(size * 2, 0, 0) + (boneAnimTable[@@LEGS_BEHIND_3_2] or emptyVector))

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

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

	@NECK_BONE_1 = 26
	@NECK_BONE_2 = 27
	@NECK_BONE_3 = 28
	@NECK_BONE_4 = 29

	@LEGS_FRONT_1 = 19
	@LEGS_FRONT_2 = 25

	@LEGS_FRONT_3 = 17
	@LEGS_FRONT_4 = 23

	@LEGS_FRONT_5 = 16
	@LEGS_FRONT_6 = 22

	@LEGS_BEHIND_1_1 = 2
	@LEGS_BEHIND_1_2 = 7

	@LEGS_BEHIND_2_1 = 4
	@LEGS_BEHIND_2_2 = 9

	@LEGS_BEHIND_3_1 = 3
	@LEGS_BEHIND_3_2 = 8

	new: (...) =>
		super(...)

if CLIENT
	hook.Add 'PPM2.SetupBones', 'PPM2.Size', (ent, data) ->
		if sizes = data\GetSizeController()
			sizes.ent = ent
			sizes\ModifyNeck()
			sizes\ModifyLegs()
			sizes.lastPAC3BoneReset = RealTime() + 1

ppm2_sv_allow_resize = ->
	for ply in *player.GetAll()
		if data = ply\GetPonyData()
			if scale = data\GetSizeController()
				scale\Reset()

cvars.AddChangeCallback 'ppm2_sv_allow_resize', ppm2_sv_allow_resize, 'PPM2.Scale'

PPM2.GetSizeController = (model = 'models/ppm/player_default_base.mdl') -> PonySizeController.AVALIABLE_CONTROLLERS[model\lower()] or PonySizeController


Make Sfx sort of work

Make Sfx sort of work

* It can load a basic sound effect file and play it now.
* Removed (commented out) HaxePunk related SoundTransform.
* Told the volume getter/setter to clam the passed in volume.
* Fixed the constructor assert.
* Changed the @@__sounds usage a bit.

require "LunoPunk.LP"

export ^

-- Sound effect object used to play embedded sounds.
class Sfx
	-- Creates a sound effect from an embedded source. Store a reference to
	-- this object so that you can play the sound using play() or loop().
	-- @param	source		The embedded sound class to use.
	-- @param	complete	Optional callback function for when the sound finishes playing.
	new: (source, complete = nil) =>
		-- Optional callback function for when the sound finishes playing.
		@complete = complete
		-- @__transform = SoundTransform!
		@__volume = 1
		@__pan = 0
		@__position = 0
		@__type = ""

		-- Sound infromation.
		@__type
		@__volume = 1
		@__pan = 0
		@__filteredVol
		@__filteredPan
		@__sound
		@__channel
		-- @__transform
		@__position = 0
		@__looping

		-- Stored Sound objects.
		@@__sounds = {}
		@@__typePlaying = {}
		-- @@__typeTransforms = {}

		assert source != nil, "Invalid source Sound."

		if type(source) == "string"
			@__sound = love.audio.newSource source, "static"
			@@__sounds[source] = @__sound
		else
			className = source.__class.__name
			@__sound = @__sounds[className]
			if @__sound == nil
				@__sound = source
				@@__sounds[className] = source

	-- Plays the sound once.
	-- @param	vol	   Volume factor, a value from 0 to 1.
	-- @param	pan	   Panning factor, a value from -1 to 1.
	-- @param   loop   If the audio should loop infinitely
	play: (volume = 1, pan = 0, loop = false) =>
		return if @__sound == nil
		@stop! if @playing!
		@__pan = LP.clamp pan, -1, 1
		@__volume = (if volume < 0 then 0 else volume)
		@__filteredPan = LP.clamp @__pan + @getPan(@__type), -1, 1
		@__filteredVol = math.max 0, @__volume * @getVolume @_type
		--@__transform.pan = @__filteredPan
		--@__transform.volume = @__filteredVol
		@__sound\setLooping loop
		@__sound\setVolume @__volume
		@__sound\play!

		if @playing!
			@addPlaying!
			@__channel.addEventListener Event.SOUND_COMPLETE, @\onComplete

		@__looping = loop
		@__position = 0

	-- Plays the sound looping. Will loop continuously until you call stop(), play(), or loop() again.
	-- @param	vol		Volume factor, a value from 0 to 1.
	-- @param	pan		Panning factor, a value from -1 to 1.
	loop: (vol = 1, pan = 0) =>
		@play vol, pan, true

	-- Stops the sound if it is currently playing.
	--
	-- @return If the sound was stopped.
	stop: =>
		return false if not @playing!
		@removePlaying!
		@__position = @__channel.position
		@__channel.removeEventListener(Event.SOUND_COMPLETE, onComplete)
		@__channel.stop()
		@__channel = nil
		true

	-- Resumes the sound from the position stop() was called on it.
	resume: =>
		@__channel = @__sound.play(@__position, if @__looping then -1 else 0, @__transform)
		if @playing!
			@addPlaying!
			@__channel.addEventListener(Event.SOUND_COMPLETE, onComplete)

		@__position = 0

	-- @private Event handler for sound completion.
	onComplete = (e = nil) =>
		if @__looping
			@loop @__volume, @__pan
		else
			@stop!

		@__position = 0
		@complete! if @complete != nil

	-- @private Add the sound to the global list.
	addPlaying = =>
		list = {}
		if not @@__typePlaying.exists(_type)
			@@__typePlaying.set(_type, list)
		else
			list = @__typePlaying.get(_type)

		list\append @

	-- @private Remove the sound from the global list. */
	removePlaying = =>
		@__typePlaying.get(@__type).remove @ if (@@__typePlaying.exists(@__type))

	-- Alter the volume factor (a value from 0 to 1) of the sound during playback.
	volume: (value) =>
		return @__volume if value == nil
		value = 0 if value < 0
		return value if @__channel == nil or @__volume == value
		@__volume = LP.clamp value, 0, 1
		filteredVol = value * @getVolume @__type
		filteredVol = 0 if filteredVol < 0
		return value if @__filteredVol == filteredVol
		@__filteredVol, @__transform.volume = filteredVol, filteredVol
		@__channel.soundTransform = @__transform
		@__volume

	-- Alter the panning factor (a value from -1 to 1) of the sound during playback.
	pan: (value) =>
		return @__pan if value == nil
		value = LP.clamp value, -1, 1
		return value if @__channel == nil or @__pan == value
		filteredPan = LP.clamp value + @getPan(@__type), -1, 1
		return value if @__filteredPan == filteredPan
		@__pan = value
		@__filteredPan, @__transform.pan = filteredPan, filteredPan
		@__channel.soundTransform = @__transform
		@__pan

	-- Change the sound type. This an arbitrary string you can use to group
	-- sounds to mute or pan en masse.
	type: (value) =>
		return @__type if value == nil or @__type == value
		if @playing!
			@removePlaying!
			@__type = value
			@addPlaying!
			-- reset, in case type has different global settings
			@pan @pan!
			@volume @volume!
		else
			@__type = value

		value

	-- If the sound is currently playing.
	playing: => @__channel != nil

	-- Position of the currently playing sound, in seconds.
	position: => (if @playing! then @__channel.position else @__position) / 1000

	-- Length of the sound, in seconds.
	length: => @__sound.length / 1000

	-- Return the global pan for a type.
	--
	-- @param	type	The type to get the pan from.
	--
	-- @return	The global pan for the type.
	getPan: (type) ->
--		if @@__typeTransforms.exists type
--			transform = @@__typeTransforms.get type
--			return if transform != nil then transform.pan else 0

		0

	-- Return the global volume for a type.
	--
	-- @param	type	The type to get the volume from.
	--
	-- @return	The global volume for the type.
	getVolume: (type) ->
--		if @@__typeTransforms.exists type
--			transform = @@__typeTransforms.get type
--			return if transform != nil then transform.volume else 1

		1

	-- Set the global pan for a type. Sfx instances of this type will add
	-- this pan to their own.
	--
	-- @param	type	The type to set.
	-- @param	pan		The pan value.
	setPan: (type, pan) ->
		transform = @__typeTransforms.get type
		if (transform == nil)
			transform = SoundTransform!
			@@__typeTransforms.set(type, transform)

		transform.pan = LP.clamp pan, -1, 1

		if @__typePlaying.exists type
			sfx.pan sfx.pan! for sfx in @@__typePlaying.get type

	-- Set the global volume for a type. Sfx instances of this type will
	-- multiply their volume by this value.
	--
	-- @param	type	The type to set.
	-- @param	volume	The volume value.
	setVolume: (type, volume) ->
		transform = @@__typeTransforms.get(type)

		if transform == nil
			transform = SoundTransform!
			@@__typeTransforms.set type, transform

		transform.volume = (if volume < 0 then 0 else volume)

		if @@__typePlaying.exists type
			sfx.volume sfx.volume! for sfx in @@__typePlaying.get type

export ^

-- Sound effect object used to play embedded sounds.
class Sfx
	-- Creates a sound effect from an embedded source. Store a reference to
	-- this object so that you can play the sound using play() or loop().
	-- @param	source		The embedded sound class to use.
	-- @param	complete	Optional callback function for when the sound finishes playing.
	new: (source, complete = nil) =>
		-- Optional callback function for when the sound finishes playing.
		@complete = complete
		@__transform = SoundTransform!
		@__volume = 1
		@__pan = 0
		@__position = 0
		@__type = ""

		-- Sound infromation.
		@__type
		@__volume = 1
		@__pan = 0
		@__filteredVol
		@__filteredPan
		@__sound
		@__channel
		@__transform
		@__position = 0
		@__looping

		-- Stored Sound objects.
		@@__sounds = {}
		@@__typePlaying = {}
		@@__typeTransforms = {}

		assert source == nil, "Invalid source Sound."

		if type source == "string"
			@__sound = Assets.getSound(source)
			@__sounds.set(source, @__sound)
		else
			className = source.__class.__name
			@__sound = @__sounds.get className
			if @__sound == nil
				@__sound = source
				@__sounds.set className, source

		this.complete = complete

	-- Plays the sound once.
	-- @param	vol	   Volume factor, a value from 0 to 1.
	-- @param	pan	   Panning factor, a value from -1 to 1.
	-- @param   loop   If the audio should loop infinitely
	play: (volume = 1, pan = 0, loop = false) =>
		return if @__sound == nil
		@stop! if @playing!
		@__pan = LP.clamp pan, -1, 1
		@__volume = (if volume < 0 then 0 else volume)
		@__filteredPan = LP.clamp @__pan + @getPan(@__type), -1, 1
		@__filteredVol = math.max 0, @__volume * @getVolume @_type
		@__transform.pan = @__filteredPan
		@__transform.volume = @__filteredVol
		@__channel = @__sound.play(0, if loop then -1 else 0, @__transform)

		if @playing!
			@addPlaying!
			@__channel.addEventListener Event.SOUND_COMPLETE, @\onComplete

		@__looping = loop
		@__position = 0

	-- Plays the sound looping. Will loop continuously until you call stop(), play(), or loop() again.
	-- @param	vol		Volume factor, a value from 0 to 1.
	-- @param	pan		Panning factor, a value from -1 to 1.
	loop: (vol = 1, pan = 0) =>
		@play vol, pan, true

	-- Stops the sound if it is currently playing.
	--
	-- @return If the sound was stopped.
	stop: =>
		return false if not @playing!
		@removePlaying!
		@__position = @__channel.position
		@__channel.removeEventListener(Event.SOUND_COMPLETE, onComplete)
		@__channel.stop()
		@__channel = nil
		true

	-- Resumes the sound from the position stop() was called on it.
	resume: =>
		@__channel = @__sound.play(@__position, if @__looping then -1 else 0, @__transform)
		if @playing!
			@addPlaying!
			@__channel.addEventListener(Event.SOUND_COMPLETE, onComplete)

		@__position = 0

	-- @private Event handler for sound completion.
	onComplete = (e = nil) =>
		if @__looping
			@loop @__volume, @__pan
		else
			@stop!

		@__position = 0
		@complete! if @complete != nil

	-- @private Add the sound to the global list.
	addPlaying = =>
		list = {}
		if not @@__typePlaying.exists(_type)
			@@__typePlaying.set(_type, list)
		else
			list = @__typePlaying.get(_type)

		list\append @

	-- @private Remove the sound from the global list. */
	removePlaying = =>
		@__typePlaying.get(@__type).remove @ if (@@__typePlaying.exists(@__type))

	-- Alter the volume factor (a value from 0 to 1) of the sound during playback.
	volume: (value) =>
		return @__volume if value == nil
		value = 0 if value < 0
		return value if @__channel == nil or @__volume == value
		@__volume = value
		filteredVol = value * @getVolume @__type
		filteredVol = 0 if filteredVol < 0
		return value if @__filteredVol == filteredVol
		@__filteredVol, @__transform.volume = filteredVol, filteredVol
		@__channel.soundTransform = @__transform
		@__volume

	-- Alter the panning factor (a value from -1 to 1) of the sound during playback.
	pan: (value) =>
		return @__pan if value == nil
		value = LP.clamp value, -1, 1
		return value if @__channel == nil or @__pan == value
		filteredPan = LP.clamp value + @getPan(@__type), -1, 1
		return value if @__filteredPan == filteredPan
		@__pan = value
		@__filteredPan, @__transform.pan = filteredPan, filteredPan
		@__channel.soundTransform = @__transform
		@__pan

	-- Change the sound type. This an arbitrary string you can use to group
	-- sounds to mute or pan en masse.
	type: (value) =>
		return @__type if value == nil or @__type == value
		if @playing!
			@removePlaying!
			@__type = value
			@addPlaying!
			-- reset, in case type has different global settings
			@pan @pan!
			@volume @volume!
		else
			@__type = value

		value

	-- If the sound is currently playing.
	playing: => @__channel != nil

	-- Position of the currently playing sound, in seconds.
	position: => (if @playing! then @__channel.position else @__position) / 1000

	-- Length of the sound, in seconds.
	length: => @__sound.length / 1000

	-- Return the global pan for a type.
	--
	-- @param	type	The type to get the pan from.
	--
	-- @return	The global pan for the type.
	getPan: (type) ->
		if @@__typeTransforms.exists type
			transform = @@__typeTransforms.get type
			return if transform != nil then transform.pan else 0

		0

	-- Return the global volume for a type.
	--
	-- @param	type	The type to get the volume from.
	--
	-- @return	The global volume for the type.
	getVolume: (type) ->
		if @@__typeTransforms.exists type
			transform = @@__typeTransforms.get type
			return if transform != nil then transform.volume else 1

		1

	-- Set the global pan for a type. Sfx instances of this type will add
	-- this pan to their own.
	--
	-- @param	type	The type to set.
	-- @param	pan		The pan value.
	setPan: (type, pan) ->
		transform = @__typeTransforms.get type
		if (transform == nil)
			transform = SoundTransform!
			@@__typeTransforms.set(type, transform)

		transform.pan = LP.clamp pan, -1, 1

		if @__typePlaying.exists type
			sfx.pan sfx.pan! for sfx in @@__typePlaying.get type

	-- Set the global volume for a type. Sfx instances of this type will
	-- multiply their volume by this value.
	--
	-- @param	type	The type to set.
	-- @param	volume	The volume value.
	setVolume: (type, volume) ->
		transform = @@__typeTransforms.get(type)

		if transform == nil
			transform = SoundTransform!
			@@__typeTransforms.set type, transform

		transform.volume = (if volume < 0 then 0 else volume)

		if @@__typePlaying.exists type
			sfx.volume sfx.volume! for sfx in @@__typePlaying.get type