From ceaada7e2d9fd7fe3648bcba12b0a7191d080a56 Mon Sep 17 00:00:00 2001 From: Nikita Kruglickiy Date: Tue, 19 Jan 2021 14:08:53 +0600 Subject: [PATCH] GUI fixes; Mesh loader WIP --- koptilnya/gui/elements/button.txt | 10 +- koptilnya/gui/elements/checkbox.txt | 10 +- koptilnya/gui/elements/element.txt | 21 ++-- koptilnya/gui/elements/label.txt | 2 +- koptilnya/gui/elements/labeled_checkbox.txt | 13 ++- koptilnya/gui/elements/panel.txt | 54 ++++++++-- koptilnya/gui/elements/radio.txt | 20 ++++ koptilnya/gui/gui.txt | 2 +- koptilnya/libs/render.txt | 17 ++- koptilnya/mesh_loader/base_entity.txt | 5 - koptilnya/mesh_loader/builder.txt | 98 ++++++++++++++---- koptilnya/mesh_loader/builder2.txt | 109 ++++++++++++++++++++ koptilnya/mesh_loader/cl_mesh.txt | 3 + koptilnya/mesh_loader/mesh.txt | 18 ++++ koptilnya/mesh_loader/parser.txt | 12 ++- koptilnya/mesh_loader/sv_mesh.txt | 97 +++++++++++++++++ 16 files changed, 433 insertions(+), 58 deletions(-) create mode 100644 koptilnya/gui/elements/radio.txt delete mode 100644 koptilnya/mesh_loader/base_entity.txt create mode 100644 koptilnya/mesh_loader/builder2.txt create mode 100644 koptilnya/mesh_loader/cl_mesh.txt create mode 100644 koptilnya/mesh_loader/mesh.txt create mode 100644 koptilnya/mesh_loader/sv_mesh.txt diff --git a/koptilnya/gui/elements/button.txt b/koptilnya/gui/elements/button.txt index 5cd95ad..27f1551 100644 --- a/koptilnya/gui/elements/button.txt +++ b/koptilnya/gui/elements/button.txt @@ -1,9 +1,7 @@ --@include label.txt --@include radius_mixin.txt ---@include /koptilnya/libs/utils.txt require("label.txt") -require("/koptilnya/libs/utils.txt") EButton = class("EButton", ELabel) EButton:include(require("radius_mixin.txt")) @@ -28,9 +26,6 @@ function EButton:initialize() }) end -function EButton:onClick() -end - function EButton:onMousePressed(x, y, key, keyName) if keyName == "MOUSE1" then self:onClick() @@ -52,3 +47,8 @@ function EButton:paint(x, y, w, h) render.setColor(self:getColorFromScheme("text")) render.drawSimpleText(x + w / 2 - textW / 2, y + h / 2 - textH / 2, self:getText()) end + +-- STUB + +function EButton:onClick() +end \ No newline at end of file diff --git a/koptilnya/gui/elements/checkbox.txt b/koptilnya/gui/elements/checkbox.txt index b7659ea..34b4fd3 100644 --- a/koptilnya/gui/elements/checkbox.txt +++ b/koptilnya/gui/elements/checkbox.txt @@ -1,8 +1,6 @@ --@include element.txt ---@include /koptilnya/libs/utils.txt require("element.txt") -require("/koptilnya/libs/utils.txt") ECheckbox = class("ECheckbox", Element) @@ -47,12 +45,14 @@ end function ECheckbox:paint(x, y, w, h) render.setColor(self:isChecked() and self:getColorFromScheme("mark") or Color(0, 0, 0, 0)) - render.drawRectFast(x, y, h, h) + render.drawRectFast(x + 4, y + 4, w - 8, h - 8) render.setColor(self:getColorFromScheme("border")) - render.drawRectOutline(x, y, h, h, 1) - render.drawRectOutline(x + 1, y + 1, h - 2, h - 2, 1) + render.drawRectOutline(x, y, w, h, 1) + render.drawRectOutline(x + 1, y + 1, w - 2, h - 2, 1) end +-- STUB + function ECheckbox:onChange(state) end \ No newline at end of file diff --git a/koptilnya/gui/elements/element.txt b/koptilnya/gui/elements/element.txt index 1d96304..8ae777e 100644 --- a/koptilnya/gui/elements/element.txt +++ b/koptilnya/gui/elements/element.txt @@ -24,7 +24,7 @@ end function Element:setWidth(width) self._width = width - self:performLayout(self:getSize()) + self:invalidateLayout() end function Element:getWidth() @@ -34,7 +34,7 @@ end function Element:setHeight(height) self._height = height - self:performLayout(self:getSize()) + self:invalidateLayout() end function Element:getHeight() @@ -56,7 +56,7 @@ function Element:setPos(x, y) end function Element:getPos() - return self._x, self._y + return self:getX(), self:getY() end function Element:getAbsolutePos(x, y) @@ -182,7 +182,7 @@ function Element:cursorIntersect(x, y) --local self:getAbsoluteBounds() local aX, aY = self:getAbsolutePos() - if x >= aX and x <= aX + self:getWidth() and y >= aY and y <= aY + self:getHeight() then + if x >= aX and x < aX + self:getWidth() and y >= aY and y < aY + self:getHeight() then return true end @@ -256,10 +256,8 @@ function Element:_postEventToAllReverse(eventKey, ...) end function Element:_onThink() - if self:isEnabled() then - if type(self.think) == "function" then - self:think() - end + if self:isEnabled() and self:isVisible() then + self:think() if self._firstChild then self._firstChild:_postEventToAllReverse("THINK") @@ -357,6 +355,13 @@ end function Element:performLayout() end +function Element:invalidateLayout() + self:performLayout(self:getSize()) +end + +function Element:think() +end + function Element:paint() end diff --git a/koptilnya/gui/elements/label.txt b/koptilnya/gui/elements/label.txt index 028f94d..3394ddf 100644 --- a/koptilnya/gui/elements/label.txt +++ b/koptilnya/gui/elements/label.txt @@ -48,7 +48,7 @@ function ELabel:getFont() return self._font end -function ELabel:getTextSize() +function ELabel:getTextSize() return self._textWidth, self._textHeight end diff --git a/koptilnya/gui/elements/labeled_checkbox.txt b/koptilnya/gui/elements/labeled_checkbox.txt index 16abb45..0c58d45 100644 --- a/koptilnya/gui/elements/labeled_checkbox.txt +++ b/koptilnya/gui/elements/labeled_checkbox.txt @@ -40,6 +40,15 @@ function ELabeledCheckbox:isChecked() return self.checkbox:isChecked() end +function ELabeledCheckbox:setEnabled(state) + self.checkbox:setEnabled(state) + self.label:setEnabled(state) +end + +function ELabeledCheckbox:isEnabled() + return self.checkbox:isEnabled() and self.label:isEnabled() +end + function ELabeledCheckbox:toggle() self.checkbox:toggle() end @@ -55,7 +64,7 @@ function ELabeledCheckbox:setFont(font) end function ELabeledCheckbox:sizeToContents() - self:performLayout(self:getSize()) + self:invalidateLayout() self:setWidth(self.label:getX() + self.label:getWidth()) self:setHeight(math.max(self.checkbox:getHeight(), self.label:getHeight())) end @@ -65,5 +74,7 @@ function ELabeledCheckbox:performLayout(w, h) self.label:setX(self.checkbox:getWidth() + self:getIndent()) end +-- STUB + function ELabeledCheckbox:onChange(state) end \ No newline at end of file diff --git a/koptilnya/gui/elements/panel.txt b/koptilnya/gui/elements/panel.txt index cf81855..5c420e8 100644 --- a/koptilnya/gui/elements/panel.txt +++ b/koptilnya/gui/elements/panel.txt @@ -16,6 +16,7 @@ function EPanel:initialize() Element.initialize(self) self._minimized = false + self._lastHeight = 0 self.title = ELabel:new() self.title:setPos(12, 8) @@ -30,17 +31,18 @@ function EPanel:initialize() } self.minimizeButton = EButton:new() - self.minimizeButton:setFont(GUI.fonts["icons"]) self.minimizeButton:setText("_") self.minimizeButton:setSize(32, 32) self.minimizeButton:setRadius(0) self.minimizeButton:setColorScheme(colorScheme) - self.minimizeButton:setEnabled(false) + self.minimizeButton.onClick = function() + self:minimizeMaximize() + end self:addChild(self.minimizeButton) self.closeButton = EButton:new() self.closeButton:setFont(GUI.fonts["icons"]) - self.closeButton:setText("X") + self.closeButton:setText(string.utf8char(10005)) self.closeButton:setSize(32, 32) self.closeButton:setRadius(0) self.closeButton:setColorScheme(colorScheme) @@ -85,8 +87,48 @@ function EPanel:close() end function EPanel:open() - self:setVisible(false) - self:setEnabled(false) + self:setVisible(true) + self:setEnabled(true) +end + +function EPanel:minimize() + self._lastHeight = self:getHeight() + + self.minimizeButton:setText(string.utf8char(10063)) + self:setMinimized(true) + self:setHeight(34) + + local child = self._firstChild + while child do + if child ~= self.title and child ~= self.minimizeButton and child ~= self.closeButton then + child:setVisible(false) + end + + child = child._nextSibling + end +end + +function EPanel:maximize() + self.minimizeButton:setText("_") + self:setMinimized(false) + self:setHeight(self._lastHeight) + + local child = self._firstChild + while child do + if child ~= self.title and child ~= self.minimizeButton and child ~= self.closeButton then + child:setVisible(true) + end + + child = child._nextSibling + end +end + +function EPanel:minimizeMaximize() + if self:isMinimized() then + self:maximize() + else + self:minimize() + end end function EPanel:onMousePressed(x, y, key, keyName) @@ -125,8 +167,8 @@ function EPanel:onMouseLeave() end function EPanel:performLayout(w, h) - self.closeButton:setPos(w - 33, 1) self.minimizeButton:setPos(w - 65, 1) + self.closeButton:setPos(w - 33, 1) end function EPanel:paint(x, y, w, h) diff --git a/koptilnya/gui/elements/radio.txt b/koptilnya/gui/elements/radio.txt new file mode 100644 index 0000000..c905fc5 --- /dev/null +++ b/koptilnya/gui/elements/radio.txt @@ -0,0 +1,20 @@ +--@include checkbox.txt +--@include /koptilnya/libs/utils.txt +--@include /koptilnya/libs/render.txt + +require("checkbox.txt") +require("/koptilnya/libs/utils.txt") +require("/koptilnya/libs/render.txt") + +ERadio = class("ERadio", ECheckbox) + +function ERadio:paint(x, y, w, h) + render.setColor(self:isChecked() and self:getColorFromScheme("mark") or Color(0, 0, 0, 0)) + render.setMaterial() + render.drawFilledCircle(x + w / 2, y + h / 2, w / 2 - 4, 6) + + render.setColor(self:getColorFromScheme("border")) + render.setMaterial() + render.drawCircle(x + w / 2, y + h / 2, w / 2) + render.drawCircle(x + w / 2, y + h / 2, w / 2 - 1) +end \ No newline at end of file diff --git a/koptilnya/gui/gui.txt b/koptilnya/gui/gui.txt index debe91a..ef05eb0 100644 --- a/koptilnya/gui/gui.txt +++ b/koptilnya/gui/gui.txt @@ -8,7 +8,7 @@ GUI = class("GUI") GUI.static.fonts = { main = render.createFont("Roboto", 16, 700, true), - icons = render.createFont("Roboto Mono", 16, 700, true) + icons = render.createFont("Roboto Mono", 24, 400, true) } function GUI:initialize(renderDevice) diff --git a/koptilnya/libs/render.txt b/koptilnya/libs/render.txt index a5fb64b..be32eff 100644 --- a/koptilnya/libs/render.txt +++ b/koptilnya/libs/render.txt @@ -29,7 +29,7 @@ function render.drawWedge(x, y, w, h, angle, mouthSize, fidelity) end end -function render.drawArc(x, y, ang, p, rad, color, seg) +function render.drawArc(x, y, ang, p, rad, seg) seg = seg || 80 ang = (-ang) + 180 local vertices = {} @@ -41,4 +41,19 @@ function render.drawArc(x, y, ang, p, rad, color, seg) end render.drawPoly(vertices) +end + +function render.drawFilledCircle(x, y, radius, seg) + local cir = {} + + table.insert(cir, { x = x, y = y, u = 0.5, v = 0.5 }) + for i = 0, seg do + local a = math.rad(( i / seg ) * -360) + table.insert(cir, { x = x + math.sin( a ) * radius, y = y + math.cos( a ) * radius, u = math.sin( a ) / 2 + 0.5, v = math.cos( a ) / 2 + 0.5 }) + end + + local a = math.rad(0) + table.insert(cir, { x = x + math.sin( a ) * radius, y = y + math.cos( a ) * radius, u = math.sin( a ) / 2 + 0.5, v = math.cos( a ) / 2 + 0.5 }) + + render.drawPoly(cir) end \ No newline at end of file diff --git a/koptilnya/mesh_loader/base_entity.txt b/koptilnya/mesh_loader/base_entity.txt deleted file mode 100644 index 63beaf8..0000000 --- a/koptilnya/mesh_loader/base_entity.txt +++ /dev/null @@ -1,5 +0,0 @@ -BaseEntity = class("BaseEntity") - -function BaseEntity:initialize() - -end \ No newline at end of file diff --git a/koptilnya/mesh_loader/builder.txt b/koptilnya/mesh_loader/builder.txt index a58d132..7accfe0 100644 --- a/koptilnya/mesh_loader/builder.txt +++ b/koptilnya/mesh_loader/builder.txt @@ -6,12 +6,17 @@ MeshBuilder = class("MeshBuilder") function MeshBuilder:initialize() self._objects = {} - self._bundle = {} + self._setups = {} if CLIENT then + self._setups = {} self._meshData = {} - - net.receive("sendHolograms", function(len) + --[[ + self._shouldBuild = false + self._shouldRevision = true + ]] + + net.receive("holograms", function(len) local hasNext = net.readBit() while hasNext == 1 do @@ -33,9 +38,47 @@ function MeshBuilder:initialize() hasNext = net.readBit() end end) + + --[[ + net.receive("revision", function() + local hasNext = net.readBit() + + while hasNext == 1 do + local key = net.readString() + + net.readEntity(function(ent) + local holo = ent:toHologram() + + self._objects[key] = holo + end) + + hasNext = net.readBit() + end + + self._shouldRevision = false + + if self._shouldBuild then + self:build() + + self._shouldBuild = false + end + end) + ]] else - - net.receive("sendObjects", function(len, ply) + --[[ + net.receive("revision", function(len, ply) + net.start("revision") + for k, v in pairs(self._objects) do + net.writeBit(1) + net.writeString(k) + net.writeEntity(v) + end + net.writeBit(0) + net.send() + end) + ]] + + net.receive("setups", function(len, ply) local hasNext = net.readBit() while hasNext == 1 do @@ -62,15 +105,15 @@ function MeshBuilder:initialize() self._objects[key] = holo end - if not self._bundle[key] then - self._bundle[key] = self._objects[key] + if not self._setups[key] then + self._setups[key] = self._objects[key] end hasNext = net.readBit() end - net.start("sendHolograms") - for k, v in pairs(self._bundle) do + net.start("holograms") + for k, v in pairs(self._setups) do net.writeBit(1) net.writeString(k) net.writeEntity(v) @@ -78,7 +121,7 @@ function MeshBuilder:initialize() net.writeBit(0) net.send(ply) - table.empty(self._bundle) + table.empty(self._setups) end) end end @@ -89,8 +132,8 @@ if CLIENT then end function MeshBuilder:setup(key, pos, ang, scale, color, mat, parent, relativeTo) - if not self._objects[key] and not self._bundle[key] and self._meshData[key] then - self._bundle[key] = { + if not self._objects[key] and not self._setups[key] and self._meshData[key] then + self._setups[key] = { pos = pos, ang = ang, scale = scale, @@ -109,9 +152,20 @@ if CLIENT then end function MeshBuilder:build() - local function send(objects) - net.start("sendObjects") - for k, v in pairs(objects) do + --[[ + if self._shouldRevision then + net.start("revision") + net.send() + + self._shouldBuild = true + + return + end + ]] + + local function sendSetups(setups) + net.start("setups") + for k, v in pairs(setups) do net.writeBit(1) net.writeString(k) net.writeVector(v.pos) @@ -126,28 +180,28 @@ if CLIENT then net.send() end - local bundleKeys = table.getKeys(self._bundle) + local bundleKeys = table.getKeys(self._setups) if #bundleKeys > BUNDLE_SIZE then local i = 1 - timer.create("send", SEND_DELAY, math.ceil(#bundleKeys / BUNDLE_SIZE), function() + timer.create("sendSetups", SEND_DELAY, math.ceil(#bundleKeys / BUNDLE_SIZE), function() local from = (i - 1) * BUNDLE_SIZE + 1 - local objects = {} + local setups = {} for _, v in pairs({ unpack(bundleKeys, from, from + BUNDLE_SIZE - 1) }) do - objects[v] = self._bundle[v] + setups[v] = self._setups[v] end - send(objects) + sendSetups(setups) if i < math.ceil(#bundleKeys / BUNDLE_SIZE) then i = i + 1 else - table.empty(self._bundle) + table.empty(self._setups) end end) else - send(self._bundle) + send(self._setups) end end end \ No newline at end of file diff --git a/koptilnya/mesh_loader/builder2.txt b/koptilnya/mesh_loader/builder2.txt new file mode 100644 index 0000000..26f0f37 --- /dev/null +++ b/koptilnya/mesh_loader/builder2.txt @@ -0,0 +1,109 @@ +local MODEL_PLACEHOLDER = "models/holograms/cube.mdl" + +MeshBuilder = class("MeshBuilder") + +function MeshBuilder:initialize() + self._holograms = {} + + if SERVER then + self._meshObjects = {} + self._setupAllArgs = nil + + net.receive("meshDataSet", function(_, ply) + self._meshObjects = net.readTable() + + if self._setupAllArgs ~= nil then + self:setupAll(unpack(self._setupAllArgs)) + + self._setupAllArgs = nil + end + + net.start("holograms") + for k, v in pairs(self._holograms) do + net.writeBit(1) + net.writeString(k) + net.writeEntity(v) + end + + net.writeBit(0) + net.send(ply) + end) + else + self._meshData = {} + self._hologramsReceived = false + + net.receive("holograms", function() + local hasNext = net.readBit() + + while hasNext == 1 do + local key = net.readString() + + net.readEntity(function(ent) + if not isValid(ent) then return end + + if self._meshData[key] and not self._holograms[key] then + self._holograms[key] = ent:toHologram() + end + end) + + hasNext = net.readBit() + end + + self._hologramsReceived = true + + self:applyMeshes() + end) + end +end + +if SERVER then + function MeshBuilder:setup(key, pos, ang, scale, color, mat, parent, relativeTo) + if isValid(self._holograms[key]) then return end + + if isValid(relativeTo) then + pos = relativeTo:localToWorld(pos) + ang = relativeTo:localToWorldAngles(ang) + end + + local holo = holograms.create(pos, ang, MODEL_PLACEHOLDER, scale) + holo:setColor(color) + holo:setMaterial(mat) + holo:setParent(parent) + + self._meshes[key] = Mesh:new() + self._holograms[key] = holo + + return holo + end + + function MeshBuilder:setupAll(pos, ang, scale, color, mat, parent, relativeTo) + if #self._meshObjects > 0 then + for _, v in pairs(self._meshObjects) do + self:setup(v, pos, ang, scale, color, mat, parent, relativeTo) + end + else + self._setupAllArgs = {pos, ang, scale, color, mat, parent, relativeTo} + end + end +else + function MeshBuilder:setMeshData(meshData) + self._meshData = meshData + + if self._hologramsReceived then + self:applyMeshes() + else + net.start("meshDataSet") + net.writeTable(table.getKeys(meshData)) + net.send() + end + end + + function MeshBuilder:applyMeshes() + for k, v in pairs(self._holograms) do + if isValid(v) then + v:setMesh(self._meshData[k]) + v:setRenderBounds(Vector(-200), Vector(200)) + end + end + end +end \ No newline at end of file diff --git a/koptilnya/mesh_loader/cl_mesh.txt b/koptilnya/mesh_loader/cl_mesh.txt new file mode 100644 index 0000000..1a409c3 --- /dev/null +++ b/koptilnya/mesh_loader/cl_mesh.txt @@ -0,0 +1,3 @@ +--@include /koptilnya/libs/utils.txt + +require("/koptilnya/libs/utils.txt") \ No newline at end of file diff --git a/koptilnya/mesh_loader/mesh.txt b/koptilnya/mesh_loader/mesh.txt new file mode 100644 index 0000000..c6a803f --- /dev/null +++ b/koptilnya/mesh_loader/mesh.txt @@ -0,0 +1,18 @@ +--@include /koptilnya/libs/utils.txt + +require("/koptilnya/libs/utils.txt") + +Mesh = class("Mesh") + +accessorFunc(Mesh, "_entity", "Entity", chip()) + +if SERVER then + +else + function Mesh:setData(data) + self:getEntity():setMesh(data) + self:getEntity():setRenderBounds(Vector(-200), Vector(200)) + end +end + +--function Mesh:get \ No newline at end of file diff --git a/koptilnya/mesh_loader/parser.txt b/koptilnya/mesh_loader/parser.txt index b20011e..44b3061 100644 --- a/koptilnya/mesh_loader/parser.txt +++ b/koptilnya/mesh_loader/parser.txt @@ -2,14 +2,19 @@ local MAX_QUOTA = 0.75 Parser = class("Parser") -function Parser:initialize(link) +local initialChipName = chip():getChipName() +local function setStatus(status) + setName(string.format("%s (%s)", initialChipName, status)) +end + +function Parser:initialize(link) if CLIENT then local triangles = mesh.trianglesLeft() local createFromObjCoroutine = coroutine.create(function(objData) self:OnLoaded(objData, mesh.createFromObj(objData, true)) end) - print("Getting file...") + setStatus("Getting file...") http.get(link, function(objData) local loadMesh = coroutine.wrap(function() self.meshData = mesh.createFromObj(objData, true) @@ -17,10 +22,11 @@ function Parser:initialize(link) return true end) - print("File received, start parsing...") + setStatus("File received, start parsing...") hook.add("think", "loadingMesh", function() while quotaAverage() < quotaMax() * MAX_QUOTA do if loadMesh() then + setName(initialChipName) self:onLoaded(objData, self.meshData, triangles - mesh.trianglesLeft()) hook.remove("think", "loadingMesh") diff --git a/koptilnya/mesh_loader/sv_mesh.txt b/koptilnya/mesh_loader/sv_mesh.txt new file mode 100644 index 0000000..09164fc --- /dev/null +++ b/koptilnya/mesh_loader/sv_mesh.txt @@ -0,0 +1,97 @@ +--@include /koptilnya/libs/utils.txt + +require("/koptilnya/libs/utils.txt") + +accessorFunc(Mesh, "_relativeTo", "RelativeTo", nil) + +function Mesh:initialize(pos, ang, scale, color, mat, parent, relativeTo) + --create holo + + self:setPos(pos) + self:setAng(ang) + self:setScale(scale) + self:setColor(color) + self:setMaterial(mat) + self:setParent(parent) + self:setRelativeTo(relativeTo) +end + +function Mesh:getPos() + return self:getEntity():getPos() +end + +function Mesh:setPos(pos) + self:getEntity():setPos(pos) +end + +function Mesh:getRelativePos() + if isValid(self:getRelativeTo()) then + return self:getRelativeTo():worldToLocal(self:getPos()) + else + return self:getPos() + end +end + +function Mesh:setRelativePos(pos) + if isValid(self:getRelativeTo()) then + self:setPos(self:getRelativeTo():localToWorld(pos)) + else + self:setPos(pos) + end +end + +function Mesh:getAng() + return self:getEntity():getAng() +end + +function Mesh:setAng(pos) + self:getEntity():setAng(pos) +end + +function Mesh:getRelativeAng() + if isValid(self:getRelativeTo()) then + return self:getRelativeTo():worldToLocalAngles(self:getAng()) + else + return self:getAng() + end +end + +function Mesh:setRelativeAng(ang) + if isValid(self:getRelativeTo()) then + self:setAng(self:getRelativeTo():localToWorldAngles(ang)) + else + self:setAng(ang) + end +end + +function Mesh:getScale() + return self:getEntity():getScale() +end + +function Mesh:setScale(scale) + self:getEntity():setScale(scale) +end + +function Mesh:getColor() + return self:getEntity():getColor() +end + +function Mesh:setColor(color) + self:getEntity():setColor(color) +end + +function Mesh:getMaterial() + return self:getEntity():getMaterial() +end + +function Mesh:setMaterial(material) + self:getEntity():setMaterial(material) +end + +function Mesh:getParent() + return self:getEntity():getParent() +end + +function Mesh:setParent(parent) + self:getEntity():setParent(parent) +end \ No newline at end of file