Set mask with helper function in Entity's constructor

require "LunoPunk.graphics.Graphiclist"
require "LunoPunk.LP"
require "LunoPunk.Mask"
require "LunoPunk.Tweener"
require "LunoPunk.utils.moonscript"
require "LunoPunk.utils.Math"

export ^

class Entity extends Tweener
	-- Constructor. Can be usd to place the Entity and assign a graphic and mask.
	-- @param	x			X position to place the Entity.
	-- @param	y			Y position to place the Entity.
	-- @param	graphic		Graphic to assign to the Entity.
	-- @param	mask		Mask to assign to the Entity.
	new: (x = 0, y = 0, graphic = nil, mask = nil) =>
		super!
		-- If the Entity should render.
		@visible = true
		-- If the Entity should respond to collision checks.
		@collidable = true
		-- If the entity should follow the camera.
		@followCamera = false
		-- X position of the Entity in the Scene.
		@__x = x
		-- Y position of the Entity in the Scene.
		@__y = y
		-- X origin of the Entity's hitbox.
		@originX = 0
		-- Y origin of the Entity's hitbox.
		@originY = 0
		-- Width of the Entity's hitbox.
		@width = 0
		-- Height of the Entity's hitbox.
		@height = 0

		-- The bottommost position of the Entity's hitbox.
		@bottom = 0
		-- The center x position of the Entity's hitbox.
		@centerX = 0
		-- The center y position of the Entity's hitbox.
		@centerY = 0
		-- The collision type, used for collision checking.
		@__typeNext = nil
		@__typePrev = nil
		@__type = ""
		@name = ""

		@__point = LP.point
		@__camera = LP.point2

		-- Collision information.
		@__hitbox = Mask!
		-- An optional Mask component, used for specialized collision. If this is not assigned, collision checks will use the Entity's hitbox by default.
		@mask mask if mask != nil
		@__moveX, @__moveY = 0, 0
		@__collide_x, @__collide_y = 0, 0

		-- The rendering layer of this Entity. Higher layers are rendered first.
		@__layer = LP.BASELAYER!

		-- Graphical component to render to the screen.
		@__graphic = graphic if graphic != nil

		@__hitbox\assignTo @

	-- X position of the Entity in the Scene.
	x: (value) =>
		if value != nil
			@__x = value
			return @__x

		if @followCamera
			@__x + LP.camera.x
		else
			@__x

	-- Y position of the Entity in the Scene.
	y: (value) =>
		if value != nil
			@__y = value
			return @__y
		if @followCamera
			@__y + LP.camera.y
		else
			@__y

	-- Some helper accessors

	-- If the Entity collides with the camera rectangle.
	onCamera: =>
		return false if @__scene == nil
		@collideRect @x!, @y!, @__scene.camera.x, @__scene.camera.y, LP.width, LP.height

	-- Half the Entity's width.
	halfWidth: => @width/2
	-- Half the Entity's height.
	halfHeight: => @height/2
	-- The center x position of the Entity's hitbox.
	centerX: => @x! - @originX + @halfWidth!
	-- The center y position of the Entity's hitbox.
	centerY: => @y! - @originY + @halfHeight!
	-- The leftmost position of the Entity's hitbox.
	left: => @x! - @originX
	-- The rightmost position of the Entity's hitbox.
	right: => @x! - @originX + @width
	-- The topmost position of the Entity's hitbox.
	top: => @y! - @originY
	-- The bottommost position of the Entity's hitbox.
	bottom: => @y! - @originY + @height

	layer: (value) =>
		return @__layer if value == nil

		@__graphic.layer = value if @__graphic != nil

		if @__scene == nil
			@__layer = value
			return @__layer

		@__scene\removeRender @
		@__layer = if value then value else nil
		@__scene\addRender @
		@__layer

	type: (value) =>
		return @__type if value == nil

		if @__scene == nil
			@__type = value
			return @__type

		@__scene\removeType(@) if @__type != ""
		@__type = value
		@__scene\addType(@) if @__type != ""

		@__type

	mask: (value) =>
		return @__mask if value == nil or @__mask == value

		@__mask\assignTo(nil) if @__mask != nil
		@__mask = if value then value else nil
		@__mask\assignTo(@) if value

		@__mask

	graphic: (value) =>
		return @__graphic if value == nil or @__graphic == value

		value.layer = @__layer if value
		@__graphic = if value then value else nil
		@__graphic

	scene: (value) =>
		@__scene = value if value != nil and @__scene != value
		@__scene

	-- The World object is deprecated for FlashPunk-like compatibility
	-- TODO give a massage about this kept for porting compatibility
	world: (value) =>
		@__scene = value if value != nil
		@__scene

	-- Override this, called when the Entity is added to a Scene.
	added: =>
	-- Override this, called when the Entity is removed from a Scene.
	removed: =>
	-- Updates the Entity.
	update: => super!

	-- Renders the Entity. If you override this for special behaviour,
	-- remember to call super.render() to render the Entity's graphic.
	render: =>
		if @__graphic != nil and @__graphic.visible
			if @__graphic.relative
				@__point.x = @x!
				@__point.y = @y!
			else
				@__point.x, @__point.y = 0, 0
				@__camera.x = if @__scene == nil then LP.camera.x else @__scene.camera.x
				@__camera.y = if @__scene == nil then LP.camera.y else @__scene.camera.y

			@__graphic\render @__point, @__camera

	-- Checks for a collision against an Entity type.
	-- @param	type		The Entity type to check for.
	-- @param	x			Virtual x position to place this Entity.
	-- @param	y			Virtual y position to place this Entity.
	-- @return	The first Entity collided with, or null if none were collided.
	collide: (type, x, y) =>
		return nil if @__scene == nil

		return nil if not @collidable or @__scene\typeFirst! == nil
		@__collide_x, @__collide_y = @x!, @y!
		@x x
		@y y
		e = @__scene\typeFirst!

		if @__mask == nil
			while e != nil
				c = do
					c1 = e.collidable and e != @
					c2 = x - @originX + @width > e\x! - e.originX
					c3 = y - @originY + @height > e\y! - e.originY
					c4 = x - @originX < e\x! - e.originX + e.width
					c5 = y - @originY < e\y! - e.originY + e.height
					c1 and c2 and c3 and c4 and c5
				if c
					if e.__mask == nil or e.__mask\collide @HITBOX
						@x @__collide_x
						@y @__collide_y
						return e

				e = e.__typeNext

			@x @__collide_x
			@y @__colide_y

			return nil

		while e != nil
			c = do
				c1 = e.collidable and e != @
				c2 = x - @originX + @width > e.x - e.originX
				c3 = y - @originY + @height > e.y - e.originY
				c4 = x - @originX < e.x - e.originX + e.width
				c5 = y - @originY < e.y - e.originY + e.height
				c1 and c2 and c3 and c4 and c5
			if c
				if e.__mask\collide (if e.__mask != nil then e.__mask else e.HITBOX)
					@x @__collide_x
					@y @__collide_y
					return e

			e = e.__typeNext

		@x @__collide_x
		@y @__collide_y

		return nil

	-- Checks for collision against multiple Entity types.
	-- @param	types		An Array or Vector of Entity types to check for.
	-- @param	x			Virtual x position to place this Entity.
	-- @param	y			Virtual y position to place this Entity.
	-- @return	The first Entity collided with, or null if none were collided.
	collideTypes: (types, x, y) =>
		return nil if @__scene == nil

		if type(types) == "string"
			return @collide types, x, y
		else
			if types != nil
				for t in *types
					e = @collide t, x, y
					return e if e != nil

		return nil

	-- Checks if this Entity collides with a specific Entity.
	-- @param	e		The Entity to collide against.
	-- @param	x		Virtual x position to place this Entity.
	-- @param	y		Virtual y position to place this Entity.
	-- @return	The Entity if they overlap, or null if they don't.
	collideWith: (e, x, y) =>
		@__collide_x, @__collide_y = @x!, @y!
		@x x
		@y y

		c = do
			c1 = @collidable and e.collidable
			c2 = x - @originX + @width > e.x - e.originX
			c3 = y - @originY + @height > e.y - e.originY
			c4 = x - @originX < e.x - e.originX + e.width
			c5 = y - @originY < e.y - e.originY + e.height
			c1 and c2 and c3 and c4 and c5
		if c
			if @__mask == nil
				if e.__mask == nil or e.__mask\collide @__hitbox
					@x @__collide_x
					@y @__collide_y
					return e
				@x @__collide_x
				@y @__collide_y
				return nil

			if (if @__mask\collide(e.__mask) != nil then e.__mask else e.__hitbox)
				@x @__collide_x
				@y @__collide_y
				return e

		@x @__collide_x
		@y @__collide_y
		return nil

	-- Checks if this Entity overlaps the specified rectangle.
	-- @param	x			Virtual x position to place this Entity.
	-- @param	y			Virtual y position to place this Entity.
	-- @param	rX			X position of the rectangle.
	-- @param	rY			Y position of the rectangle.
	-- @param	rWidth		Width of the rectangle.
	-- @param	rHeight		Height of the rectangle.
	-- @return	If they overlap.
	collideRect: (x, y, rX, rY, rWidth, rHeight) =>
		c = do
			c1 = x - @originX + @width >= rX
			c2 = y - @originY + @height >= rY
			c3 = x - @originX <= rX + rWidth
			c4 = y - @originY <= rY + rHeight
			c1 and c2 and c3 and c4
		if c
			return true if @__mask == nil
			@__collide_x, @__collide_y = @x!, @y!
			@x x
			@y y
			LP.entity.x = rX
			LP.entity.y = rY
			LP.entity.width = rWidth
			LP.entity.height = rHeight
			if @__mask\collide LP.entity.__hitbox
				@x @__collide_x
				@y @__collide_y
				return true

			@x @__collide_x
			@y @__collide_y
			return false

		return false

	-- Checks if this Entity overlaps the specified position.
	-- @param	x			Virtual x position to place this Entity.
	-- @param	y			Virtual y position to place this Entity.
	-- @param	pX			X position.
	-- @param	pY			Y position.
	-- @return	If the Entity intersects with the position.
	collidePoint: (x, y, pX, pY) =>
		c = do
			c1 = pX >= x - @originX
			c2 = pY >= y - @originY
			c3 = pX < x - @originX + @width
			c4 = pY < y - @originY + height
		if c
			return true if @__mask == nil
			@__collide_x, @__collide_y = @x!, @y!
			@x x
			@y y
			LP.entity.x, LP.entity.y = pX, pY
			LP.entity.width, LP.entity.height = 1, 1
			if @__mask\collide LP.entity.__hitbox
				@x @__collide_x
				@y @__collide_y
				return true

			@x @__collide_x
			@y @__collide_y
			return false

		return false

	-- Populates an array with all collided Entities of a type.
	-- @param	type		The Entity type to check for.
	-- @param	x			Virtual x position to place this Entity.
	-- @param	y			Virtual y position to place this Entity.
	-- @param	array		The Array or Vector object to populate.
	-- @return	The array, populated with all collided Entities.
	collideInto: (type, x, y, array) =>
		return if @__scene == nil

		e = @__scene.__typeFirst\get type
		return if not @collidable or e == nil

		@__collide_x, @__collide_y = @x!, @y!
		@x x
		@y y

		if @__mask == nil
			while e != nil
				c = do
					c1 = e.collidable and e != @
					c2 = x - @originX + @width > e.x - e.originX
					c3 = y - @originY + @height > e.y - e.originY
					c4 = x - @originX < e.x - e.originX + e.width
					c5 = y - @originY < e.y - e.originY + e.height
					c1 and c2 and c3 and c4 and c5
				if c
					array[#array+1] = e if e__mask == nil or e.__mask\collide @__hitbox

				e = e.__typeNext

			@x @__collide_x
			@y @__collide_y
			return

		while e != nil
			c = do
				c1 = e.collidable and e != @
				c2 = x - @originX + @width > e.x - e.originX
				c3 = y - @originY + @height > e.y - e.originY
				c4 = x - @originX < e.x - e.originX + e.width
				c5 = y - @originY < e.y - e.originY + e.height
				c1 and c2 and c3 and c4 and c5
			if c
				array[#array+1] = e if (if @__mask\collide(e.__mask) != nil then e.__mask else e.__hitbox)

			e = e.__typeNext

		@x @__collide_x
		@y @__collide_y
		return

	-- Populates an array with all collided Entities of multiple types.
	-- @param	types		An array of Entity types to check for.
	-- @param	x			Virtual x position to place this Entity.
	-- @param	y			Virtual y position to place this Entity.
	-- @param	array		The Array or Vector object to populate.
	-- @return	The array, populated with all collided Entities.
	collideTypesInto: (types, x, y, array) =>
		return if @__scene == nil
		for t in *types
			@collideInto t, x, y, array

	-- Adds the graphic to the Entity via a Graphiclist.
	-- @param	g		Graphic to add.
	addGraphic: (g) =>
		g.layer = @__layer
		if @__graphic == nil
			@graphic g
		else if moon_type(g) == Graphiclist
			@graphic!\add g
		else
			list = Graphiclist!
			list\add graphic if @graphic! != nil
			list\add g
			@graphic list
			list\setEntity @

		g

	-- Sets the Entity's hitbox properties.
	-- @param	width		Width of the hitbox.
	-- @param	height		Height of the hitbox.
	-- @param	originX		X origin of the hitbox.
	-- @param	originY		Y origin of the hitbox.
	setHitbox: (width, height, originX, originY) =>
		@width, @height = width, height
		@originX, @originY = originX, originY

	-- Sets the Entity's hitbox to match that of the provided object.
	-- @param	o		The object defining the hitbox (eg. an Image or Rectangle).
	setHitboxTo: (o) =>
		@width, @height = o.width, o.height

		if o.originX and o.originY
			@originX, @originY = o.originX, o.originY
		else
			@originX, @originY = o.x, o.y
			@originX, @originY = -@originX, -@originY

	-- Sets the origin of the Entity.
	-- @param	x		X origin.
	-- @param	y		Y origin.
	setOrigin: (x, y) => @originX, @originY = x, y

	-- Center's the Entity's origin (half width & height).
	centerOrigin: => @originX, @originY = @halfWidth!, @halfHeight!

	-- Calculates the distance from another Entity.
	-- @param	e				The other Entity.
	-- @param	useHitboxes		If hitboxes should be used to determine the distance. If not, the Entities' x/y positions are used.
	-- @return	The distance.
	distanceFrom: (e, useHitboxes) =>
		return math.sqrt (@x! - e.x) * (@x! - e.x) + (@y! - e.y) * (@y - e.y) if not useHitboxes
		LP.distanceRects @x! - @originX, @y! - @originY, @width, @height, e.x - e.originX, e.y - e.originY, e.width, e.height

	-- Calculates the distance from this Entity to the point.
	-- @param	px				X position.
	-- @param	py				Y position.
	-- @param	useHitboxes		If hitboxes should be used to determine the distance. If not, the Entities' x/y positions are used.
	-- @return	The distance.
	distanceToPoint: (px, py, useHitbox) =>
		return math.sqrt (@x! - px) * (@x! - px) + (@y! - py) * (@y! - py) if not useHitbox
		LP.distanceRectPoint px, py, @x! - @originX, @y! - @originY, @width, @height

	-- Calculates the distance from this Entity to the rectangle.
	-- @param	rx			X position of the rectangle.
	-- @param	ry			Y position of the rectangle.
	-- @param	rwidth		Width of the rectangle.
	-- @param	rheight		Height of the rectangle.
	-- @return	The distance.
	distanceToRect: (rx, ry, rwidth, rheight) =>
		LP.distanceRects rx, ry, rwidth, rheight, @x! - @originX, @y! - @originY, @width, @height

	-- Gets the class name as a string.
	-- @return	A string representing the class name.
	toString: => @@.__name

	__tostring: => @toString!

	-- Moves the Entity by the amount, retaining integer values for its x and y.
	-- @param	x			Horizontal offset.
	-- @param	y			Vertical offset.
	-- @param	solidType	An optional collision type to stop flush against upon collision.
	-- @param	sweep		If sweeping should be used (prevents fast-moving objects from going through solidType).
	moveBy: (x, y, solidType, sweep) =>
		@__moveX += x
		@__moveY += y
		x, y = Math.round(@__moveX), Math.round(@__moveY)
		@__moveX -= x
		@__moveY -= y

		if solidType != nil
			if x != 0
				if @collidable and (sweep or @collideTypes(solidType, @x! + x, @y!) != nil)
					sign = if x > 0 then 1 else -1
					while x != 0
						e = @collideTypes solidType, @x! + sign, @y!
						if e != nil
							break if @moveCollideX e
							@x @x! + sign
						else
							@x @x! + sign

						x -= sign
				else
					@x @x! + x

			if y != 0
				if @collidable and (sweep or @collideTypes(solidType, @x!, @y! + y) != nil)
					sign = if y > 0 then 1 else -1
					while y != 0
						e = @collideTypes solidType, @x!, @y! + sign
						if e != nil
							break if @moveCollideY e
							@y @y! + sign
						else
							@y @y! + sign

						y -= sign
		else
			@x @x! + x
			@y @y! + y

	-- Moves the Entity to the position, retaining integer values for its x and y.
	-- @param	x			X position.
	-- @param	y			Y position.
	-- @param	solidType	An optional collision type to stop flush against upon collision.
	-- @param	sweep		If sweeping should be used (prevents fast-moving objects from going through solidType).
	moveTo: (x, y, solidType, sweep) => moveBy x - @x!, y - @y!, solidType, sweep

	-- Moves towards the target position, retaining integer values for its x and y.
	-- @param	x			X target.
	-- @param	y			Y target.
	-- @param	amount		Amount to move.
	-- @param	solidType	An optional collision type to stop flush against upon collision.
	-- @param	sweep		If sweeping should be used (prevents fast-moving objects from going through solidType).
	moveTowards: (x, y, amount, solidType, sweep) =>
		@__point.x, @__point.y = x - @x!, y - @y!
		@__point.normalize amount if @__point.x * @__point.x + @__point.y * @__point.y > amount * amount
		@moveBy @__point.x, @__point.y, solidType, sweep

	-- Moves at an angle by a certain amount, retaining integer values for its x and y.
	-- @param	angle		Angle to move at in degrees.
	-- @param	amount		Amount to move.
	-- @param	solidType	An optional collision type to stop flush against upon collision.
	-- @param	sweep		If sweeping should be used (prevents fast-moving objects from going through solidType).
	moveAtAngle: (angle, amount, solidType, sweep) =>
		angle *= LP.RAD!
		@moveBy math.cos(angle) * amount, math.sin(angle) * amount, solidType, sweep

	-- When you collide with an Entity on the x-axis with moveTo() or moveBy().
	-- @param	e		The Entity you collided with.
	moveCollideX: (e) => true

	-- When you collide with an Entity on the y-axis with moveTo() or moveBy().
	-- @param	e		The Entity you collided with.
	moveCollideY: (e) => true

	-- Clamps the Entity's hitbox on the x-axis.
	-- @param	left		Left bounds.
	-- @param	right		Right bounds.
	-- @param	padding		Optional padding on the clamp.
	clampHorizontal: (left, right, padding) =>
		@x left + @originX + padding if @x! - @originX < left + padding
		@x right - @width + @originX - padding if @x! - @originX + @width > right - padding

	-- Clamps the Entity's hitbox on the y axis.
	-- @param	top			Min bounds.
	-- @param	bottom		Max bounds.
	-- @param	padding		Optional padding on the clamp.
	clampVertical: (top, bottom, padding) =>
		@y top + @originY + padding if @y! - @originY < top + padding
		@y bottom - @height + @originX - padding if @y! - @originY + @height > bottom - padding

	-- Center graphic inside bounding rect.
	centerGraphicInRect: =>
		@__graphic.x, @__graphic.y = @halfWidth!, @halfHeight! if @__graphic != nil

require "LunoPunk.graphics.Graphiclist"
require "LunoPunk.LP"
require "LunoPunk.Mask"
require "LunoPunk.Tweener"
require "LunoPunk.utils.moonscript"
require "LunoPunk.utils.Math"

export ^

class Entity extends Tweener
	-- Constructor. Can be usd to place the Entity and assign a graphic and mask.
	-- @param	x			X position to place the Entity.
	-- @param	y			Y position to place the Entity.
	-- @param	graphic		Graphic to assign to the Entity.
	-- @param	mask		Mask to assign to the Entity.
	new: (x = 0, y = 0, graphic = nil, mask = nil) =>
		super!
		-- If the Entity should render.
		@visible = true
		-- If the Entity should respond to collision checks.
		@collidable = true
		-- If the entity should follow the camera.
		@followCamera = false
		-- X position of the Entity in the Scene.
		@__x = x
		-- Y position of the Entity in the Scene.
		@__y = y
		-- X origin of the Entity's hitbox.
		@originX = 0
		-- Y origin of the Entity's hitbox.
		@originY = 0
		-- Width of the Entity's hitbox.
		@width = 0
		-- Height of the Entity's hitbox.
		@height = 0

		-- The bottommost position of the Entity's hitbox.
		@bottom = 0
		-- The center x position of the Entity's hitbox.
		@centerX = 0
		-- The center y position of the Entity's hitbox.
		@centerY = 0
		-- The collision type, used for collision checking.
		@__typeNext = nil
		@__typePrev = nil
		@__type = ""
		@name = ""

		@__point = LP.point
		@__camera = LP.point2

		-- Collision information.
		@__hitbox = Mask!
		-- An optional Mask component, used for specialized collision. If this is not assigned, collision checks will use the Entity's hitbox by default.
		@__mask = mask if mask != nil
		@__moveX, @__moveY = 0, 0
		@__collide_x, @__collide_y = 0, 0

		-- The rendering layer of this Entity. Higher layers are rendered first.
		@__layer = LP.BASELAYER!

		-- Graphical component to render to the screen.
		@__graphic = graphic if graphic != nil

		@__hitbox\assignTo @

	-- X position of the Entity in the Scene.
	x: (value) =>
		if value != nil
			@__x = value
			return @__x

		if @followCamera
			@__x + LP.camera.x
		else
			@__x

	-- Y position of the Entity in the Scene.
	y: (value) =>
		if value != nil
			@__y = value
			return @__y
		if @followCamera
			@__y + LP.camera.y
		else
			@__y

	-- Some helper accessors

	-- If the Entity collides with the camera rectangle.
	onCamera: =>
		return false if @__scene == nil
		@collideRect @x!, @y!, @__scene.camera.x, @__scene.camera.y, LP.width, LP.height

	-- Half the Entity's width.
	halfWidth: => @width/2
	-- Half the Entity's height.
	halfHeight: => @height/2
	-- The center x position of the Entity's hitbox.
	centerX: => @x! - @originX + @halfWidth!
	-- The center y position of the Entity's hitbox.
	centerY: => @y! - @originY + @halfHeight!
	-- The leftmost position of the Entity's hitbox.
	left: => @x! - @originX
	-- The rightmost position of the Entity's hitbox.
	right: => @x! - @originX + @width
	-- The topmost position of the Entity's hitbox.
	top: => @y! - @originY
	-- The bottommost position of the Entity's hitbox.
	bottom: => @y! - @originY + @height

	layer: (value) =>
		return @__layer if value == nil

		@__graphic.layer = value if @__graphic != nil

		if @__scene == nil
			@__layer = value
			return @__layer

		@__scene\removeRender @
		@__layer = if value then value else nil
		@__scene\addRender @
		@__layer

	type: (value) =>
		return @__type if value == nil

		if @__scene == nil
			@__type = value
			return @__type

		@__scene\removeType(@) if @__type != ""
		@__type = value
		@__scene\addType(@) if @__type != ""

		@__type

	mask: (value) =>
		return @__mask if value == nil or @__mask == value

		@__mask\assignTo(nil) if @__mask != nil
		@__mask = if value then value else nil
		@__mask\assignTo(@) if value

		@__mask

	graphic: (value) =>
		return @__graphic if value == nil or @__graphic == value

		value.layer = @__layer if value
		@__graphic = if value then value else nil
		@__graphic

	scene: (value) =>
		@__scene = value if value != nil and @__scene != value
		@__scene

	-- The World object is deprecated for FlashPunk-like compatibility
	-- TODO give a massage about this kept for porting compatibility
	world: (value) =>
		@__scene = value if value != nil
		@__scene

	-- Override this, called when the Entity is added to a Scene.
	added: =>
	-- Override this, called when the Entity is removed from a Scene.
	removed: =>
	-- Updates the Entity.
	update: => super!

	-- Renders the Entity. If you override this for special behaviour,
	-- remember to call super.render() to render the Entity's graphic.
	render: =>
		if @__graphic != nil and @__graphic.visible
			if @__graphic.relative
				@__point.x = @x!
				@__point.y = @y!
			else
				@__point.x, @__point.y = 0, 0
				@__camera.x = if @__scene == nil then LP.camera.x else @__scene.camera.x
				@__camera.y = if @__scene == nil then LP.camera.y else @__scene.camera.y

			@__graphic\render @__point, @__camera

	-- Checks for a collision against an Entity type.
	-- @param	type		The Entity type to check for.
	-- @param	x			Virtual x position to place this Entity.
	-- @param	y			Virtual y position to place this Entity.
	-- @return	The first Entity collided with, or null if none were collided.
	collide: (type, x, y) =>
		return nil if @__scene == nil

		return nil if not @collidable or @__scene\typeFirst! == nil
		@__collide_x, @__collide_y = @x!, @y!
		@x x
		@y y
		e = @__scene\typeFirst!

		if @__mask == nil
			while e != nil
				c = do
					c1 = e.collidable and e != @
					c2 = x - @originX + @width > e\x! - e.originX
					c3 = y - @originY + @height > e\y! - e.originY
					c4 = x - @originX < e\x! - e.originX + e.width
					c5 = y - @originY < e\y! - e.originY + e.height
					c1 and c2 and c3 and c4 and c5
				if c
					if e.__mask == nil or e.__mask\collide @HITBOX
						@x @__collide_x
						@y @__collide_y
						return e

				e = e.__typeNext

			@x @__collide_x
			@y @__colide_y

			return nil

		while e != nil
			c = do
				c1 = e.collidable and e != @
				c2 = x - @originX + @width > e.x - e.originX
				c3 = y - @originY + @height > e.y - e.originY
				c4 = x - @originX < e.x - e.originX + e.width
				c5 = y - @originY < e.y - e.originY + e.height
				c1 and c2 and c3 and c4 and c5
			if c
				if e.__mask\collide (if e.__mask != nil then e.__mask else e.HITBOX)
					@x @__collide_x
					@y @__collide_y
					return e

			e = e.__typeNext

		@x @__collide_x
		@y @__collide_y

		return nil

	-- Checks for collision against multiple Entity types.
	-- @param	types		An Array or Vector of Entity types to check for.
	-- @param	x			Virtual x position to place this Entity.
	-- @param	y			Virtual y position to place this Entity.
	-- @return	The first Entity collided with, or null if none were collided.
	collideTypes: (types, x, y) =>
		return nil if @__scene == nil

		if type(types) == "string"
			return @collide types, x, y
		else
			if types != nil
				for t in *types
					e = @collide t, x, y
					return e if e != nil

		return nil

	-- Checks if this Entity collides with a specific Entity.
	-- @param	e		The Entity to collide against.
	-- @param	x		Virtual x position to place this Entity.
	-- @param	y		Virtual y position to place this Entity.
	-- @return	The Entity if they overlap, or null if they don't.
	collideWith: (e, x, y) =>
		@__collide_x, @__collide_y = @x!, @y!
		@x x
		@y y

		c = do
			c1 = @collidable and e.collidable
			c2 = x - @originX + @width > e.x - e.originX
			c3 = y - @originY + @height > e.y - e.originY
			c4 = x - @originX < e.x - e.originX + e.width
			c5 = y - @originY < e.y - e.originY + e.height
			c1 and c2 and c3 and c4 and c5
		if c
			if @__mask == nil
				if e.__mask == nil or e.__mask\collide @__hitbox
					@x @__collide_x
					@y @__collide_y
					return e
				@x @__collide_x
				@y @__collide_y
				return nil

			if (if @__mask\collide(e.__mask) != nil then e.__mask else e.__hitbox)
				@x @__collide_x
				@y @__collide_y
				return e

		@x @__collide_x
		@y @__collide_y
		return nil

	-- Checks if this Entity overlaps the specified rectangle.
	-- @param	x			Virtual x position to place this Entity.
	-- @param	y			Virtual y position to place this Entity.
	-- @param	rX			X position of the rectangle.
	-- @param	rY			Y position of the rectangle.
	-- @param	rWidth		Width of the rectangle.
	-- @param	rHeight		Height of the rectangle.
	-- @return	If they overlap.
	collideRect: (x, y, rX, rY, rWidth, rHeight) =>
		c = do
			c1 = x - @originX + @width >= rX
			c2 = y - @originY + @height >= rY
			c3 = x - @originX <= rX + rWidth
			c4 = y - @originY <= rY + rHeight
			c1 and c2 and c3 and c4
		if c
			return true if @__mask == nil
			@__collide_x, @__collide_y = @x!, @y!
			@x x
			@y y
			LP.entity.x = rX
			LP.entity.y = rY
			LP.entity.width = rWidth
			LP.entity.height = rHeight
			if @__mask\collide LP.entity.__hitbox
				@x @__collide_x
				@y @__collide_y
				return true

			@x @__collide_x
			@y @__collide_y
			return false

		return false

	-- Checks if this Entity overlaps the specified position.
	-- @param	x			Virtual x position to place this Entity.
	-- @param	y			Virtual y position to place this Entity.
	-- @param	pX			X position.
	-- @param	pY			Y position.
	-- @return	If the Entity intersects with the position.
	collidePoint: (x, y, pX, pY) =>
		c = do
			c1 = pX >= x - @originX
			c2 = pY >= y - @originY
			c3 = pX < x - @originX + @width
			c4 = pY < y - @originY + height
		if c
			return true if @__mask == nil
			@__collide_x, @__collide_y = @x!, @y!
			@x x
			@y y
			LP.entity.x, LP.entity.y = pX, pY
			LP.entity.width, LP.entity.height = 1, 1
			if @__mask\collide LP.entity.__hitbox
				@x @__collide_x
				@y @__collide_y
				return true

			@x @__collide_x
			@y @__collide_y
			return false

		return false

	-- Populates an array with all collided Entities of a type.
	-- @param	type		The Entity type to check for.
	-- @param	x			Virtual x position to place this Entity.
	-- @param	y			Virtual y position to place this Entity.
	-- @param	array		The Array or Vector object to populate.
	-- @return	The array, populated with all collided Entities.
	collideInto: (type, x, y, array) =>
		return if @__scene == nil

		e = @__scene.__typeFirst\get type
		return if not @collidable or e == nil

		@__collide_x, @__collide_y = @x!, @y!
		@x x
		@y y

		if @__mask == nil
			while e != nil
				c = do
					c1 = e.collidable and e != @
					c2 = x - @originX + @width > e.x - e.originX
					c3 = y - @originY + @height > e.y - e.originY
					c4 = x - @originX < e.x - e.originX + e.width
					c5 = y - @originY < e.y - e.originY + e.height
					c1 and c2 and c3 and c4 and c5
				if c
					array[#array+1] = e if e__mask == nil or e.__mask\collide @__hitbox

				e = e.__typeNext

			@x @__collide_x
			@y @__collide_y
			return

		while e != nil
			c = do
				c1 = e.collidable and e != @
				c2 = x - @originX + @width > e.x - e.originX
				c3 = y - @originY + @height > e.y - e.originY
				c4 = x - @originX < e.x - e.originX + e.width
				c5 = y - @originY < e.y - e.originY + e.height
				c1 and c2 and c3 and c4 and c5
			if c
				array[#array+1] = e if (if @__mask\collide(e.__mask) != nil then e.__mask else e.__hitbox)

			e = e.__typeNext

		@x @__collide_x
		@y @__collide_y
		return

	-- Populates an array with all collided Entities of multiple types.
	-- @param	types		An array of Entity types to check for.
	-- @param	x			Virtual x position to place this Entity.
	-- @param	y			Virtual y position to place this Entity.
	-- @param	array		The Array or Vector object to populate.
	-- @return	The array, populated with all collided Entities.
	collideTypesInto: (types, x, y, array) =>
		return if @__scene == nil
		for t in *types
			@collideInto t, x, y, array

	-- Adds the graphic to the Entity via a Graphiclist.
	-- @param	g		Graphic to add.
	addGraphic: (g) =>
		g.layer = @__layer
		if @__graphic == nil
			@graphic g
		else if moon_type(g) == Graphiclist
			@graphic!\add g
		else
			list = Graphiclist!
			list\add graphic if @graphic! != nil
			list\add g
			@graphic list
			list\setEntity @

		g

	-- Sets the Entity's hitbox properties.
	-- @param	width		Width of the hitbox.
	-- @param	height		Height of the hitbox.
	-- @param	originX		X origin of the hitbox.
	-- @param	originY		Y origin of the hitbox.
	setHitbox: (width, height, originX, originY) =>
		@width, @height = width, height
		@originX, @originY = originX, originY

	-- Sets the Entity's hitbox to match that of the provided object.
	-- @param	o		The object defining the hitbox (eg. an Image or Rectangle).
	setHitboxTo: (o) =>
		@width, @height = o.width, o.height

		if o.originX and o.originY
			@originX, @originY = o.originX, o.originY
		else
			@originX, @originY = o.x, o.y
			@originX, @originY = -@originX, -@originY

	-- Sets the origin of the Entity.
	-- @param	x		X origin.
	-- @param	y		Y origin.
	setOrigin: (x, y) => @originX, @originY = x, y

	-- Center's the Entity's origin (half width & height).
	centerOrigin: => @originX, @originY = @halfWidth!, @halfHeight!

	-- Calculates the distance from another Entity.
	-- @param	e				The other Entity.
	-- @param	useHitboxes		If hitboxes should be used to determine the distance. If not, the Entities' x/y positions are used.
	-- @return	The distance.
	distanceFrom: (e, useHitboxes) =>
		return math.sqrt (@x! - e.x) * (@x! - e.x) + (@y! - e.y) * (@y - e.y) if not useHitboxes
		LP.distanceRects @x! - @originX, @y! - @originY, @width, @height, e.x - e.originX, e.y - e.originY, e.width, e.height

	-- Calculates the distance from this Entity to the point.
	-- @param	px				X position.
	-- @param	py				Y position.
	-- @param	useHitboxes		If hitboxes should be used to determine the distance. If not, the Entities' x/y positions are used.
	-- @return	The distance.
	distanceToPoint: (px, py, useHitbox) =>
		return math.sqrt (@x! - px) * (@x! - px) + (@y! - py) * (@y! - py) if not useHitbox
		LP.distanceRectPoint px, py, @x! - @originX, @y! - @originY, @width, @height

	-- Calculates the distance from this Entity to the rectangle.
	-- @param	rx			X position of the rectangle.
	-- @param	ry			Y position of the rectangle.
	-- @param	rwidth		Width of the rectangle.
	-- @param	rheight		Height of the rectangle.
	-- @return	The distance.
	distanceToRect: (rx, ry, rwidth, rheight) =>
		LP.distanceRects rx, ry, rwidth, rheight, @x! - @originX, @y! - @originY, @width, @height

	-- Gets the class name as a string.
	-- @return	A string representing the class name.
	toString: => @@.__name

	__tostring: => @toString!

	-- Moves the Entity by the amount, retaining integer values for its x and y.
	-- @param	x			Horizontal offset.
	-- @param	y			Vertical offset.
	-- @param	solidType	An optional collision type to stop flush against upon collision.
	-- @param	sweep		If sweeping should be used (prevents fast-moving objects from going through solidType).
	moveBy: (x, y, solidType, sweep) =>
		@__moveX += x
		@__moveY += y
		x, y = Math.round(@__moveX), Math.round(@__moveY)
		@__moveX -= x
		@__moveY -= y

		if solidType != nil
			if x != 0
				if @collidable and (sweep or @collideTypes(solidType, @x! + x, @y!) != nil)
					sign = if x > 0 then 1 else -1
					while x != 0
						e = @collideTypes solidType, @x! + sign, @y!
						if e != nil
							break if @moveCollideX e
							@x @x! + sign
						else
							@x @x! + sign

						x -= sign
				else
					@x @x! + x

			if y != 0
				if @collidable and (sweep or @collideTypes(solidType, @x!, @y! + y) != nil)
					sign = if y > 0 then 1 else -1
					while y != 0
						e = @collideTypes solidType, @x!, @y! + sign
						if e != nil
							break if @moveCollideY e
							@y @y! + sign
						else
							@y @y! + sign

						y -= sign
		else
			@x @x! + x
			@y @y! + y

	-- Moves the Entity to the position, retaining integer values for its x and y.
	-- @param	x			X position.
	-- @param	y			Y position.
	-- @param	solidType	An optional collision type to stop flush against upon collision.
	-- @param	sweep		If sweeping should be used (prevents fast-moving objects from going through solidType).
	moveTo: (x, y, solidType, sweep) => moveBy x - @x!, y - @y!, solidType, sweep

	-- Moves towards the target position, retaining integer values for its x and y.
	-- @param	x			X target.
	-- @param	y			Y target.
	-- @param	amount		Amount to move.
	-- @param	solidType	An optional collision type to stop flush against upon collision.
	-- @param	sweep		If sweeping should be used (prevents fast-moving objects from going through solidType).
	moveTowards: (x, y, amount, solidType, sweep) =>
		@__point.x, @__point.y = x - @x!, y - @y!
		@__point.normalize amount if @__point.x * @__point.x + @__point.y * @__point.y > amount * amount
		@moveBy @__point.x, @__point.y, solidType, sweep

	-- Moves at an angle by a certain amount, retaining integer values for its x and y.
	-- @param	angle		Angle to move at in degrees.
	-- @param	amount		Amount to move.
	-- @param	solidType	An optional collision type to stop flush against upon collision.
	-- @param	sweep		If sweeping should be used (prevents fast-moving objects from going through solidType).
	moveAtAngle: (angle, amount, solidType, sweep) =>
		angle *= LP.RAD!
		@moveBy math.cos(angle) * amount, math.sin(angle) * amount, solidType, sweep

	-- When you collide with an Entity on the x-axis with moveTo() or moveBy().
	-- @param	e		The Entity you collided with.
	moveCollideX: (e) => true

	-- When you collide with an Entity on the y-axis with moveTo() or moveBy().
	-- @param	e		The Entity you collided with.
	moveCollideY: (e) => true

	-- Clamps the Entity's hitbox on the x-axis.
	-- @param	left		Left bounds.
	-- @param	right		Right bounds.
	-- @param	padding		Optional padding on the clamp.
	clampHorizontal: (left, right, padding) =>
		@x left + @originX + padding if @x! - @originX < left + padding
		@x right - @width + @originX - padding if @x! - @originX + @width > right - padding

	-- Clamps the Entity's hitbox on the y axis.
	-- @param	top			Min bounds.
	-- @param	bottom		Max bounds.
	-- @param	padding		Optional padding on the clamp.
	clampVertical: (top, bottom, padding) =>
		@y top + @originY + padding if @y! - @originY < top + padding
		@y bottom - @height + @originX - padding if @y! - @originY + @height > bottom - padding

	-- Center graphic inside bounding rect.
	centerGraphicInRect: =>
		@__graphic.x, @__graphic.y = @halfWidth!, @halfHeight! if @__graphic != nil


Move Entity.world to a function

Move Entity.world to a function

require "LovePunk.Tweener"

export ^

class Entity extends Tweener
	new: (x, y, graphic, mask) =>
		-- The bottommost position of the Entity's hitbox.
		@bottom = 0
		-- The center x position of the Entity's hitbox.
		@centerX = 0
		-- The center y position of the Entity's hitbox.
		@centerY = 0
		-- If the Entity should respond to collision checks.
		@collidable = false
		-- Graphical component to render to the screen.
		@graphic = nil
		-- Half the Entity's height.
		@halfHeight = 0
		-- Half the Entity's width.
		@halfWidth = 0
		-- Height of the Entity's hitbox.
		@height = 0
		-- The rendering layer of this Entity. Higher layers are rendered first.
		@layer = 0
		-- The leftmost position of the Entity's hitbox.
		@left = 0
		-- An optional Mask component, used for specialized collision. If this is not assigned, collision checks will use the Entity's hitbox by default.
		@mask = nil
		@name = ""
		-- If the Entity collides with the camera rectangle.
		@onCamera = false
		-- X origin of the Entity's hitbox.
		@originX = 0
		-- Y origin of the Entity's hitbox.
		@originY = 0
		-- The BitmapData target to draw the Entity to. Leave as null to render to the current screen buffer (default).
		@renderTarget = nil
		-- The rightmost position of the Entity's hitbox.
		@right = 0
		-- The Scene object this Entity has been added to.
		@scene = nil
		-- The topmost position of the Entity's hitbox.
		@top = 0
		-- The collision type, used for collision checking.
		@type = ""
		-- If the Entity should render.
		@visible = false
		-- Width of the Entity's hitbox.
		@width = 0
		-- X position of the Entity in the Scene.
		@x = 0
		-- Y position of the Entity in the Scene.
		@y = 0

	-- The World object is deprecated for FlashPunk-like compatibility
	world: (value) =>
		@scene = value if value != nil
		@scene

	addGraphic: (g) =>
	added: =>
	centerGraphicInRect: =>
	centerOrigin: =>
	clampHorizontal: (left, right, padding) =>
	clampVertical: (top, bottom, padding) =>
	collide: (type, x, y) =>
	collideInto: (type, x, y, array) =>
	collidePoint: (x, y, pX, pY) =>
	collideRect: (x, y, rX, rY, rWidth, rHeight) =>
	collideTypes: (types, x, y) =>
	collideTypesInto: (types, x, y, array) =>
	collideWith: (e, x, y) =>
	distanceFrom: (e, useHitboxes) =>
	distanceToPoint: (px, py, useHitbox) =>
	distanceToRect: (rx, ry, rwidth, rheight) =>
	moveAtAngle: (angle, amount, solidType, sweep) =>
	moveBy: (x, y, solidType, sweep) =>
	moveCollideX: (e) =>
	moveCollideY: (e) =>
	moveTo: (x, y, solidType, sweep) =>
	moveTowards: (x, y, amount, solidType, sweep) =>
	removed: =>
	render: =>
	setHitbox: (width, height, originX, originY) =>
	setHitboxTo: (o) =>
	setOrigin: (x, y) =>
	toString: =>
	update: =>

require "LovePunk.Tweener"

export ^

class Entity extends Tweener
	new: (x, y, graphic, mask) =>
		-- The bottommost position of the Entity's hitbox.
		@bottom = 0
		-- The center x position of the Entity's hitbox.
		@centerX = 0
		-- The center y position of the Entity's hitbox.
		@centerY = 0
		-- If the Entity should respond to collision checks.
		@collidable = false
		-- Graphical component to render to the screen.
		@graphic = nil
		-- Half the Entity's height.
		@halfHeight = 0
		-- Half the Entity's width.
		@halfWidth = 0
		-- Height of the Entity's hitbox.
		@height = 0
		-- The rendering layer of this Entity. Higher layers are rendered first.
		@layer = 0
		-- The leftmost position of the Entity's hitbox.
		@left = 0
		-- An optional Mask component, used for specialized collision. If this is not assigned, collision checks will use the Entity's hitbox by default.
		@mask = nil
		@name = ""
		-- If the Entity collides with the camera rectangle.
		@onCamera = false
		-- X origin of the Entity's hitbox.
		@originX = 0
		-- Y origin of the Entity's hitbox.
		@originY = 0
		-- The BitmapData target to draw the Entity to. Leave as null to render to the current screen buffer (default).
		@renderTarget = nil
		-- The rightmost position of the Entity's hitbox.
		@right = 0
		-- The Scene object this Entity has been added to.
		@scene = nil
		-- The topmost position of the Entity's hitbox.
		@top = 0
		-- The collision type, used for collision checking.
		@type = ""
		-- If the Entity should render.
		@visible = false
		-- Width of the Entity's hitbox.
		@width = 0
		-- The World object is deprecated
		@world = nil
		-- X position of the Entity in the Scene.
		@x = 0
		-- Y position of the Entity in the Scene.
		@y = 0

	addGraphic: (g) =>
	added: =>
	centerGraphicInRect: =>
	centerOrigin: =>
	clampHorizontal: (left, right, padding) =>
	clampVertical: (top, bottom, padding) =>
	collide: (type, x, y) =>
	collideInto: (type, x, y, array) =>
	collidePoint: (x, y, pX, pY) =>
	collideRect: (x, y, rX, rY, rWidth, rHeight) =>
	collideTypes: (types, x, y) =>
	collideTypesInto: (types, x, y, array) =>
	collideWith: (e, x, y) =>
	distanceFrom: (e, useHitboxes) =>
	distanceToPoint: (px, py, useHitbox) =>
	distanceToRect: (rx, ry, rwidth, rheight) =>
	moveAtAngle: (angle, amount, solidType, sweep) =>
	moveBy: (x, y, solidType, sweep) =>
	moveCollideX: (e) =>
	moveCollideY: (e) =>
	moveTo: (x, y, solidType, sweep) =>
	moveTowards: (x, y, amount, solidType, sweep) =>
	removed: =>
	render: =>
	setHitbox: (width, height, originX, originY) =>
	setHitboxTo: (o) =>
	setOrigin: (x, y) =>
	toString: =>
	update: =>


Implemented the perfect loader class.

Implemented the perfect loader class.

class Loader
	new: (handler) =>
		cache = {}

		meta = getmetatable(@)
		oldIndex = meta.__index

		meta.__index = (key) =>
			if value = cache[key]
				return value
			elseif value = handler(key)
				cache[key] = value
				return value
			elseif _.isFunction(oldIndex)
				return oldIndex(@, key)
			else
				return oldIndex[key]

-- class Loader
-- 	new: (@handler) =>
-- 		@cache = {}
--
-- 	__index: (key) =>
-- 		print 'hi'
--
-- 	get: (key, force = false) =>
-- 		if @cache[key] and not force then return @cache[key]
--
-- 		value = @handler(key)
-- 		@cache[key] = value
-- 		return value

return (handler) ->
	cache = {}
	__cache = {}

	setmetatable(cache, {
		__index: (key) =>
			if __cache[key] then return __cache[key]

			value = handler(key)
			cache[key] = value
			__cache[key] = value
			return value
	})

	return cache


Fixed jitting model when using render overrides

Example - PAC3 entity with hidden entity

--
-- 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
    @AVALIABLE_CONTROLLERS = {}
    @MODELS = {'models/ppm/player_default_base.mdl', 'models/ppm/player_default_base_nj.mdl', 'models/cppm/player_default_base.mdl', 'models/cppm/player_default_base_nj.mdl'}
    @__inherited: (child) =>
        child.MODELS_HASH = {mod, true for mod in *child.MODELS}
        @AVALIABLE_CONTROLLERS[mod] = child for mod in *child.MODELS
    @__inherited(@)

    @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_PACResetBones', '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
    @AVALIABLE_CONTROLLERS = {}
    @MODELS = {'models/ppm/player_default_base.mdl', 'models/ppm/player_default_base_nj.mdl', 'models/cppm/player_default_base.mdl', 'models/cppm/player_default_base_nj.mdl'}
    @__inherited: (child) =>
        child.MODELS_HASH = {mod, true for mod in *child.MODELS}
        @AVALIABLE_CONTROLLERS[mod] = child for mod in *child.MODELS
    @__inherited(@)

    @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()
        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_PACResetBones', '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